題目大意:
給一棵樹,每次激活或熄滅一個點,每次問這些點都聯通起來所需的最小總邊權
分析:
若根據dfs序給所有點排序,為$v1,v2,v3....vk$,那么答案就是$(dis(v1,v2)+dis(v2,v3)+...+dis(vk-1,vk)+dis(vk,v1))/2$
只需要動態的維護這個序列,每次拿出前后兩個點后用lca修改答案即可
這道題主要是想學習一下set在這方面的使用
畢竟現在開O2的題比比皆是,要是能少寫個平衡樹豈不美哉(我也不會寫233
我們就默認使用的是c++11的標準吧
如何為set重載運算符
首先你要有一個結構體,并在里面重載一個bool類型的()函數,先這樣(這里以將數字按d數組的大小排序為例):
struct cmp{bool operator () (const int &a,const int &b){return d[a]<d[b];}};
接下來這樣聲明set:
set<int,cmp>S;
如何查找一個元素的前驅后繼等
我們這里聲明迭代器時使用c++11特有的auto用法,看起來方便不少(set<int>::iterator)
這里以查找x的前驅后繼為例(循環式的,即若x為最后一個數,后繼就是第一個數,反過來同理
auto it=S.lower_bound(x),a=it,b=it; int l=(it==S.begin()?*--S.end():*--a); int r=(it==--S.end()?*S.begin():*++b);
注意這里end()函數返回的是個超尾,所以要--
這里我們看到迭代器的移動用加減即可,取值時用*即可
插入刪除
insert和erase,千萬別記錯了
S.insert(x);
S.erase(x);
接下來放上這道題的代碼


1 #include<bits/stdc++.h> 2 #define N 100005 3 #define ll long long 4 using namespace std; 5 int n,q; 6 int h[N],to[2*N],nxt[2*N],w[2*N],etop; 7 void add(int a,int b,int c){to[++etop]=b,nxt[etop]=h[a],w[etop]=c,h[a]=etop;} 8 int fa[N][20],d[N],tot,dep[N]; 9 ll len[N][20]; 10 void dfs(int u){ 11 d[u]=++tot; 12 for(int i=1;i<=17;i++){ 13 fa[u][i]=fa[fa[u][i-1]][i-1]; 14 len[u][i]=len[u][i-1]+len[fa[u][i-1]][i-1]; 15 if(fa[u][i]==0)break; 16 } 17 for(int k=h[u],v=to[k];k;k=nxt[k],v=to[k]) 18 if(v!=fa[u][0]){ 19 fa[v][0]=u;len[v][0]=w[k]; 20 dep[v]=dep[u]+1; 21 dfs(v); 22 } 23 } 24 ll LCA(int x,int y){ 25 ll ans=0; 26 if(dep[x]<dep[y])swap(x,y); 27 for(int i=17;i>=0;i--) 28 if(fa[x][i]&&dep[fa[x][i]]>=dep[y]){ 29 ans+=len[x][i]; 30 x=fa[x][i]; 31 } 32 if(x==y)return ans; 33 for(int i=17;i>=0;i--) 34 if(fa[x][i]!=fa[y][i]){ 35 ans+=len[x][i]; 36 ans+=len[y][i]; 37 x=fa[x][i]; 38 y=fa[y][i]; 39 } 40 ans+=len[x][0]; 41 ans+=len[y][0]; 42 return ans; 43 } 44 struct cmp{bool operator () (const int &a,const int &b){return d[a]<d[b];}}; 45 set<int,cmp>S; 46 ll ans; 47 void ins(int x){ 48 S.insert(x); 49 auto it=S.lower_bound(x),a=it,b=it; 50 int l=(it==S.begin()?*--S.end():*--a); 51 int r=(it==--S.end()?*S.begin():*++b); 52 ans-=LCA(l,r); 53 ans+=LCA(l,x); 54 ans+=LCA(x,r); 55 } 56 void del(int x){ 57 auto it=S.lower_bound(x),a=it,b=it; 58 int l=(it==S.begin()?*--S.end():*--a); 59 int r=(it==--S.end()?*S.begin():*++b); 60 ans+=LCA(l,r); 61 ans-=LCA(l,x); 62 ans-=LCA(x,r); 63 S.erase(x); 64 } 65 char o[2]; 66 int main(){ 67 scanf("%d",&n); 68 for(int i=1,a,b,c;i<n;i++){ 69 scanf("%d%d%d",&a,&b,&c); 70 add(a,b,c),add(b,a,c); 71 } 72 dfs(1); 73 scanf("%d",&q); 74 while(q--){ 75 scanf("%s",o); 76 if(o[0]=='?')cout<<ans/2<<endl; 77 else{ 78 int x;scanf("%d",&x); 79 if(o[0]=='+')ins(x); 80 else del(x); 81 } 82 } 83 return 0; 84 }
?
2019.6.18 update
今天在寫一個掃描線題時使用上面的方法重置set的比較符出現了問題,使用lower_bound會在大數據下WA掉,但upper_bound則沒問題
如果使用常規的重載運算符的話則兩種方式都沒問題……(蒙圈ing
這里給個當時的兩種比較函數吧
這是出了問題的:
struct cmp{bool operator () (const hu &A,const hu &B){double as=A.y+sqrt(A.r*A.r-(A.x-X)*(A.x-X))*(1.0*A.u);double bs=B.y+sqrt(B.r*B.r-(B.x-X)*(B.x-X))*(1.0*B.u);if(as+eps>bs&&bs+eps>as)return A.u<B.u;else return as<bs;} };
這個是改成重載運算符的:
bool operator < (const hu &A,const hu &B){double as=A.y+sqrt(A.r*A.r-(A.x-X)*(A.x-X))*(1.0*A.u);double bs=B.y+sqrt(B.r*B.r-(B.x-X)*(B.x-X))*(1.0*B.u);if(as+eps>bs&&bs+eps>as)return A.u<B.u;else return as<bs; }
如果哪位大神路過希望能指點一下555(;′д`)ゞ
?
還有NOI沒有C++11
所以跟我一起拼一遍
iterator
再來3遍
iterator
iterator
iterator
ojbk!
?