P2056 [ZJOI2007]捉迷藏
題意:
也許直接看Qtree4的題面描述會更好些。大概就是給一棵樹,最初樹上的點都是黑點,有若干操做能夠改變點的狀態,問任意兩個黑點之間的最大距離是多少。
題解:
動態點分不錯的題啊,也是我作的第二道動態點分。
想到兩個黑點之間的最大距離必定是能夠由兩條鏈拼起來,因此咱們在每一個點維護鏈的集合,每次取最長+次長就行了。
考慮動態點分,建出點分樹。
總:我的感受動態點分和點分幾乎不是一個難度的啊~。動態點分的題更好,也更難,不像我作的那幾道點分那麼無聊。html
固然,動態點分治從題型上來看就是原本一個靜態很好求的東西它一會改個點權什麼的。因而它就動態了。node
說到底動態點分治仍是和點分治仍是有必定的聯繫的。至於我說的點分標準套路函數它應該除了$solve()$沒有了以外,其餘還都有保留。功能大概有一些區別。ios
但其實動態點分也很套路,它也有幾個很是很是套路的函數與很是很是套路的解題步驟。只是題靈活的多。數組
動態點分經常使用函數:數據結構
1.$find-rt(int\;x,int\;fa)$ 和點分的同名函數一個做用,找樹重心。app
2.$solve$_$work$_$tree(int\;u)$寫法和做用相似點分中的$work()$,但它主要是爲了建出點分樹。ide
3.$dfs(int\;u,int\;fa)$ 也是爲了統計最初的答案,同時最主要的是爲了求出每一個點到它在點分樹上各級父親的距離。函數
4.$change(int\;x,int\;y)$ 固然從這個開始並非必定有的了,可是大多數改點權的可能會存在這個函數,用來把$x$的權值改成$y$。學習
5.$query(int\;x)$ 用來求以$x$爲根的時候求的一些問題。固然,具體狀況具體分析。ui
動態點分經常使用思路:
1.標準套路,先建出點分樹,而後要計算最初的時候的權值,以及每一個點分樹上的點表明的那棵子樹對於點分樹上的父親的貢獻。而且要求出到各級點分樹父親的距離。
補:點分樹:還記得咱們點分時寫的那個$work()$函數嗎?只要把重心按深度相連就會變成一棵點分樹了。
點分樹上的點並不必定是按原樹的深度排的深度,徹底有可能一個點在點分樹上的父親
在原樹中的深度大於它。
注意點分樹上每個點記錄的值表明原樹一棵子樹
2.而後就能夠處理修改了,具體作法是從這個點開始往上找它點分樹上的父親。每次到一個點一般都會改兩個值,一個是本點的權值,一個是子樹對於點分樹父親的貢獻。
3.詢問也是相似,只須要不停跳父親,而後每次加上大重心所表明的子樹中除小重心所表明的子樹外其它點對該點的貢獻並減去它子樹對於父親的貢獻。貌似有點抽象,看題吧。
動態點分解決問題:
每次有一棵樹,而後動態改點權或點的狀態,而後求以不一樣點爲根時的答案或其它的。總之跟樹上的距離有關或跟樹鏈有關。
話很少說,咱們經過幾道例題來具體講解:
題意:
給一顆n個節點的樹,邊權均爲1,初始點權均爲0,m次操做:
Q x:詢問x的點權。
M x d w:將樹上與節點x距離不超過d的節點的點權均加上w。
題解:
這是我最開始學動態點分時井然學長留的兩道題之一,另外一道題和它如出一轍。表示十分不滿。
這是一道動態點分比較板子的一道題。動態點分可能配合一個數據結構的,這道題配合的是線段樹。
下面咱們來分開每一個函數看看動態點分的套路。
1 void find_rt(int u,int fa){ 2 wgt[u]=1;f[u]=0; 3 int t=indexx[u],vv; 4 while(t){ 5 vv=edge[t].v; 6 if(!vist[vv] && vv!=fa){ 7 find_rt(vv,u); 8 wgt[u]+=wgt[vv]; 9 f[u]=max(f[u],wgt[vv]); 10 } 11 t=edge[t].nxt; 12 } 13 f[u]=max(f[u],sum-wgt[u]); 14 if(f[u]<f[Root]) Root=u; 15 }
找重心標準操做,再也不多說了。
1 void build_work_tree(int u){ 2 vist[u]=1; 3 dfs(u,0,0); 4 int t=indexx[u],vv; 5 while(t){ 6 vv=edge[t].v; 7 if(!vist[vv]){ 8 Root=0;sum=wgt[vv]; 9 find_rt(vv,u); 10 fa[Root]=u; 11 build_work_tree(Root); 12 } 13 t=edge[t].nxt; 14 } 15 }
這個是建點分樹的過程,具體步驟相似點分中的$solve()$,只是須要記錄一下它在點分樹上的父親。
1 void dfs(int u,int f,int dep){ 2 dist[u].push_back(dep); 3 int t=indexx[u],vv; 4 while(t){ 5 vv=edge[t].v; 6 if(!vist[vv] && vv!=f){ 7 dfs(vv,u,dep+1); 8 } 9 t=edge[t].nxt; 10 } 11 }
這道題的$dfs$並無太多功能,大概是動態點分的基本操做,就是記錄$u$到它的每一級父親的距離。
具體過程就像棧同樣,先遍歷到它的是它的最高父親,而後是次級父親......最後是本身,也就是0級父親。
可是這個函數執行完以後跟咱們想要的0級父親,一級父親......是反的,因此咱們再用一個函數把它正過來。
1 void init(){ 2 for(int i=1;i<=n;i++){ 3 int len=dist[i].size(); 4 for(int j=0;j<len;j++){ 5 if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]); 6 else break; 7 } 8 } 9 }
這樣更方便在點分樹上跳的同時計算距離。
而後就要到修改操做與查詢操做了,中間咱們先插入一些線段樹的函數,方便一會研究修改。
1 void push_up(int now){ 2 int l=tree[now].lson; 3 int r=tree[now].rson; 4 tree[now].sum=tree[l].sum+tree[r].sum; 5 } 6 void add(int &now,int p,int x,int l,int r){ 7 if(!now) now=++siz; 8 if(l==r){ 9 tree[now].sum+=x; 10 return; 11 } 12 int mid=(l+r)/2; 13 if(p<=mid) add(tree[now].lson,p,x,l,mid); 14 else add(tree[now].rson,p,x,mid+1,r); 15 push_up(now); 16 } 17 int ask(int now,int ql,int qr,int l,int r){ 18 if(!now) return 0; 19 if(ql<=l && r<=qr){ 20 return tree[now].sum; 21 } 22 int mid=(l+r)/2,ret=0; 23 if(ql<=mid) ret+=ask(tree[now].lson,ql,qr,l,mid); 24 if(qr>mid) ret+=ask(tree[now].rson,ql,qr,mid+1,r); 25 return ret; 26 }
動態開點線段樹,$ask()$區間求和,$add()$是單點修改。注意線段樹數組必定要開很大很大。
而後是重點了。
1 void Add(int x,int y,int z){ 2 int st=x,cnt=0,temp; 3 while(x){ 4 temp=dist[st][cnt]; 5 if(temp<=y) add(root[x][0],y-temp,z,0,n); 6 if(fa[x]){ 7 temp=dist[st][cnt+1]; 8 if(temp<=y) add(root[x][1],y-temp,z,0,n); 9 } 10 x=fa[x];cnt++; 11 } 12 }
這個表明原題目中的吸引皮皮鼠。動態點分比較標準的修改操做之一。
動態點分的修改一般都是從本身開始向上跳父親,每次都要記錄本身這棵子樹內的點對於父親子樹的貢獻。
具體爲何你們畫畫圖理解一下,一會到查詢可能看起來還能好點。
到了這道題就是表明在這個大重心的距離爲$y-temp$之內的點可能受到影響,因爲單點修改區間查詢比較好寫,我就沒寫區間修改。
1 void Query(int x){ 2 int st=x,cnt=0,temp; 3 ans=0; 4 while(x){ 5 temp=dist[st][cnt]; 6 ans+=ask(root[x][0],temp,n,0,n); 7 if(fa[x]){ 8 temp=dist[st][cnt+1]; 9 ans-=ask(root[x][1],temp,n,0,n); 10 } 11 x=fa[x],cnt++; 12 } 13 printf("%d\n",ans); 14 }
這個就是查詢操做了,看起來和修改也差很少。
每次要減去本身這棵子樹內對父親子樹的影響,避免同一棵子樹內的修改操做影響查詢不少次。
那麼這道題就完美解決了,你們有沒有學會動態點分呢?
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <vector> 5 #define N 200011 6 #define INF 0x7f7f7f7f 7 using namespace std; 8 struct apple{ 9 int v,nxt; 10 }edge[N*4]; 11 struct node{ 12 int lson,rson,sum; 13 }tree[12800011*2]; 14 vector<int> dist[N]; 15 int indexx[N],tot,ans,siz,wgt[N],f[N],Root,root[N][2],sum,n,m,vist[N]; 16 int fa[N]; 17 char s[111]; 18 void addedge(int x,int y){ 19 edge[++tot].v=y; 20 edge[tot].nxt=indexx[x]; 21 indexx[x]=tot; 22 } 23 void push_up(int now){ 24 int l=tree[now].lson; 25 int r=tree[now].rson; 26 tree[now].sum=tree[l].sum+tree[r].sum; 27 } 28 void add(int &now,int p,int x,int l,int r){ 29 if(!now) now=++siz; 30 if(l==r){ 31 tree[now].sum+=x; 32 return; 33 } 34 int mid=(l+r)/2; 35 if(p<=mid) add(tree[now].lson,p,x,l,mid); 36 else add(tree[now].rson,p,x,mid+1,r); 37 push_up(now); 38 } 39 int ask(int now,int ql,int qr,int l,int r){ 40 if(!now) return 0; 41 if(ql<=l && r<=qr){ 42 return tree[now].sum; 43 } 44 int mid=(l+r)/2,ret=0; 45 if(ql<=mid) ret+=ask(tree[now].lson,ql,qr,l,mid); 46 if(qr>mid) ret+=ask(tree[now].rson,ql,qr,mid+1,r); 47 return ret; 48 } 49 void find_rt(int u,int fa){ 50 wgt[u]=1;f[u]=0; 51 int t=indexx[u],vv; 52 while(t){ 53 vv=edge[t].v; 54 if(!vist[vv] && vv!=fa){ 55 find_rt(vv,u); 56 wgt[u]+=wgt[vv]; 57 f[u]=max(f[u],wgt[vv]); 58 } 59 t=edge[t].nxt; 60 } 61 f[u]=max(f[u],sum-wgt[u]); 62 if(f[u]<f[Root]) Root=u; 63 } 64 void dfs(int u,int f,int dep){ 65 dist[u].push_back(dep); 66 int t=indexx[u],vv; 67 while(t){ 68 vv=edge[t].v; 69 if(!vist[vv] && vv!=f){ 70 dfs(vv,u,dep+1); 71 } 72 t=edge[t].nxt; 73 } 74 } 75 void build_work_tree(int u){ 76 vist[u]=1; 77 dfs(u,0,0); 78 int t=indexx[u],vv; 79 while(t){ 80 vv=edge[t].v; 81 if(!vist[vv]){ 82 Root=0;sum=wgt[vv]; 83 find_rt(vv,u); 84 fa[Root]=u; 85 build_work_tree(Root); 86 } 87 t=edge[t].nxt; 88 } 89 } 90 void Add(int x,int y,int z){ 91 int st=x,cnt=0,temp; 92 while(x){ 93 temp=dist[st][cnt]; 94 if(temp<=y) add(root[x][0],y-temp,z,0,n); 95 if(fa[x]){ 96 temp=dist[st][cnt+1]; 97 if(temp<=y) add(root[x][1],y-temp,z,0,n); 98 } 99 x=fa[x];cnt++; 100 } 101 } 102 void Query(int x){ 103 int st=x,cnt=0,temp; 104 ans=0; 105 while(x){ 106 temp=dist[st][cnt]; 107 ans+=ask(root[x][0],temp,n,0,n); 108 if(fa[x]){ 109 temp=dist[st][cnt+1]; 110 ans-=ask(root[x][1],temp,n,0,n); 111 } 112 x=fa[x],cnt++; 113 } 114 printf("%d\n",ans); 115 } 116 void init(){ 117 for(int i=1;i<=n;i++){ 118 int len=dist[i].size(); 119 for(int j=0;j<len;j++){ 120 if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]); 121 else break; 122 } 123 } 124 } 125 int main(){ 126 freopen("a.txt","r",stdin); 127 int x,y,z; 128 scanf("%d%d",&n,&m); 129 for(int i=1;i<n;i++){ 130 scanf("%d%d",&x,&y); 131 addedge(x,y); 132 addedge(y,x); 133 } 134 f[0]=INF;sum=n;Root=0; 135 find_rt(1,0); 136 build_work_tree(Root); 137 init(); 138 for(int i=1;i<=m;i++){ 139 scanf("%s",s); 140 if(s[0]=='M'){ 141 scanf("%d%d%d",&x,&y,&z); 142 Add(x,y,z); 143 } 144 else{ 145 scanf("%d",&x); 146 Query(x); 147 } 148 } 149 return 0; 150 }
題意:
也許直接看Qtree4的題面描述會更好些。大概就是給一棵樹,最初樹上的點都是黑點,有若干操做能夠改變點的狀態,問任意兩個黑點之間的最大距離是多少。
題解:
動態點分不錯的題啊,也是我作的第二道動態點分。
想到兩個黑點之間的最大距離必定是能夠由兩條鏈拼起來,因此咱們在每一個點維護鏈的集合,每次取最長+次長就行了。
考慮動態點分,建出點分樹。
因爲咱們應該避免最長和次長出如今同一棵子樹內,因此咱們只把該子樹內的最長鏈貢獻給父親。
注意本棵子樹內到小重心的最長鏈不必定是到大重心的最長鏈!因此應該分開維護。
這樣的話若是是修改就從本身開始在維護的小子樹集合中插入或刪除,並修改對於大子樹的貢獻。
採用一個很強的操做避免掉set——雙堆。
每次想要刪除就在刪除堆中插入,查詢時若是發現兩堆堆頂相同就同時彈掉直到不一樣或彈空。
因爲我當時剛學因此代碼十分混亂還請見諒!
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <queue> 5 #include <vector> 6 #define INF 2000000000 7 #define N 200011 8 using namespace std; 9 struct apple{ 10 int v,nxt; 11 }edge[N*4]; 12 vector<int> dist[N]; 13 int vist[N],f[N],siz[N],Anum[N],Bnum[N],sum,rt,fa[N],ans,indexx[N],tot,n,m,dp[N],sit[N]; 14 priority_queue<int> A[N],B[N],D[N],E[N],C; 15 char s[111]; 16 inline int read(){ 17 int f=1,ret=0;char ch=getchar(); 18 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 19 while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();} 20 return ret; 21 } 22 void addedge(int x,int y){ 23 edge[++tot].v=y; 24 edge[tot].nxt=indexx[x]; 25 indexx[x]=tot; 26 } 27 void find_rt(int u,int ff){ 28 int t,vv; 29 t=indexx[u]; 30 siz[u]=1;f[u]=0; 31 while(t){ 32 vv=edge[t].v; 33 if(!vist[vv] && vv!=ff){ 34 find_rt(vv,u); 35 siz[u]+=siz[vv]; 36 f[u]=max(f[u],siz[vv]); 37 } 38 t=edge[t].nxt; 39 } 40 f[u]=max(f[u],sum-siz[u]); 41 if(f[u]<f[rt]) rt=u; 42 } 43 void basic_dfs(int anc,int top,int u,int f,int dep){ 44 int t,vv; 45 dist[u].push_back(dep); 46 B[top].push(dep); 47 Bnum[top]++; 48 t=indexx[u]; 49 while(t){ 50 vv=edge[t].v; 51 if(!vist[vv] && vv!=f){ 52 basic_dfs(anc,top,vv,u,dep+1); 53 } 54 t=edge[t].nxt; 55 } 56 } 57 int get_max(int u,int op){ 58 if(op==1){ 59 while(!D[u].empty() && !A[u].empty() && D[u].top()==A[u].top()){ 60 D[u].pop();A[u].pop(); 61 } 62 if(!A[u].empty()) return A[u].top(); 63 else return -INF; 64 } 65 else{ 66 while(!E[u].empty() && !B[u].empty() && B[u].top()==E[u].top()){ 67 B[u].pop();E[u].pop(); 68 } 69 if(!B[u].empty()) return B[u].top(); 70 else return -INF; 71 } 72 } 73 int get_second(int u,int op){ 74 int ret; 75 if(op==1){ 76 int t=A[u].top();A[u].pop(); 77 ret=get_max(u,1); 78 A[u].push(t); 79 return ret; 80 } 81 else{ 82 int t=B[u].top();B[u].pop(); 83 ret=get_max(u,2); 84 B[u].push(t); 85 return ret; 86 } 87 } 88 void build_work_tree(int u){ 89 vist[u]=1; 90 A[u].push(0); 91 Anum[u]++; 92 dp[u]=0; 93 int t,vv; 94 t=indexx[u]; 95 while(t){ 96 vv=edge[t].v; 97 if(!vist[vv]){ 98 sum=siz[vv];rt=0; 99 find_rt(vv,u); 100 fa[rt]=u; 101 basic_dfs(u,rt,vv,0,1); 102 if(!A[u].empty()) dp[u]=max(dp[u],get_max(u,1)+get_max(rt,2)); 103 A[u].push(B[rt].top());Anum[u]++; 104 build_work_tree(rt); 105 } 106 t=edge[t].nxt; 107 } 108 A[n+1].push(dp[u]); 109 } 110 void solve(int x){ 111 sit[x]^=1; 112 D[n+1].push(dp[x]); 113 if(sit[x]){ 114 A[x].push(0); 115 Anum[x]++; 116 } 117 else{ 118 D[x].push(0); 119 Anum[x]--; 120 } 121 if(Anum[x]==0 || get_max(x,1)<0) dp[x]=-INF; 122 else if(Anum[x]==1 || get_second(x,1)<0) dp[x]=0; 123 else dp[x]=get_max(x,1)+get_second(x,1); 124 A[n+1].push(dp[x]); 125 int u=x,t=0; 126 while(fa[u]){ 127 int f=fa[u]; 128 if(Bnum[u]==0) D[fa[u]].push(-INF); 129 else if(Bnum[u]==1) D[fa[u]].push(get_max(u,2)); 130 else D[fa[u]].push(get_max(u,2)); 131 if(sit[x]){ 132 B[u].push(dist[x][t]); 133 Bnum[u]++; 134 } 135 else{ 136 E[u].push(dist[x][t]); 137 Bnum[u]--; 138 } 139 if(Bnum[u]==0) A[fa[u]].push(-INF); 140 else if(Bnum[u]==1) A[fa[u]].push(get_max(u,2)); 141 else A[fa[u]].push(get_max(u,2)); 142 D[n+1].push(dp[fa[u]]); 143 if(get_max(fa[u],1)<0) dp[fa[u]]=-INF; 144 else if(get_second(fa[u],1)<0) dp[fa[u]]=0; 145 else dp[fa[u]]=get_max(fa[u],1)+get_second(fa[u],1); 146 A[n+1].push(dp[fa[u]]); 147 u=fa[u];t++; 148 } 149 } 150 int query(int x){ 151 return get_max(n+1,1); 152 } 153 int main(){ 154 freopen("a.txt","r",stdin); 155 int x,y,z; 156 n=read(); 157 for(int i=1;i<n;i++){ 158 x=read(); 159 y=read(); 160 addedge(x,y); 161 addedge(y,x); 162 sit[i]=1; 163 } 164 sit[n]=1; 165 rt=0;f[0]=INF;sum=n; 166 find_rt(1,0); 167 build_work_tree(rt); 168 for(int i=1;i<=n;i++){ 169 int len=dist[i].size(); 170 for(int j=0;j<len;j++){ 171 if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]); 172 else break; 173 } 174 } 175 scanf("%d",&m); 176 for(int i=1;i<=m;i++){ 177 scanf("%s",s); 178 if(s[0]=='C'){ 179 x=read(); 180 solve(x); 181 } 182 else{ 183 ans=query(x); 184 if(ans<0) printf("-1\n"); 185 else printf("%d\n",ans); 186 } 187 } 188 return 0; 189 }
題意:
給出一棵邊帶權的節點數量爲n的樹,初始樹上全部節點都是白色。有兩種操做:
C x,改變節點x的顏色,即白變黑,黑變白
A,詢問樹中最遠的兩個白色節點的距離,這兩個白色節點能夠重合(此時距離爲0)。
題解:
和捉迷藏幾乎就是同樣的,多增長了邊權,有一些小細節須要修改。(好比最小值不能設0而應該是$-INF$)。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <queue> 5 #include <vector> 6 #define INF 0x7fffffff 7 #define N 100011 8 using namespace std; 9 struct apple{ 10 int v,nxt,q; 11 }edge[N*2]; 12 vector<int> dist[N]; 13 int vist[N],f[N],siz[N],Anum[N],Bnum[N],sum,rt,fa[N],ans,indexx[N],tot,n,m,dp[N],sit[N]; 14 priority_queue<int> A[N],B[N],D[N],E[N]; 15 char s[111]; 16 inline int read(){ 17 int f=1,ret=0;char ch=getchar(); 18 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 19 while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();} 20 return ret*f; 21 } 22 void addedge(int x,int y,int z){ 23 edge[++tot].v=y; 24 edge[tot].q=z; 25 edge[tot].nxt=indexx[x]; 26 indexx[x]=tot; 27 } 28 void find_rt(int u,int ff){ 29 int t,vv; 30 t=indexx[u]; 31 siz[u]=1;f[u]=0; 32 while(t){ 33 vv=edge[t].v; 34 if(!vist[vv] && vv!=ff){ 35 find_rt(vv,u); 36 siz[u]+=siz[vv]; 37 f[u]=max(f[u],siz[vv]); 38 } 39 t=edge[t].nxt; 40 } 41 f[u]=max(f[u],sum-siz[u]); 42 if(f[u]<f[rt]) rt=u; 43 } 44 void basic_dfs(int anc,int top,int u,int f,int dep){ 45 int t,vv; 46 dist[u].push_back(dep); 47 B[top].push(dep); 48 Bnum[top]++; 49 t=indexx[u]; 50 while(t){ 51 vv=edge[t].v; 52 if(!vist[vv] && vv!=f){ 53 basic_dfs(anc,top,vv,u,dep+edge[t].q); 54 } 55 t=edge[t].nxt; 56 } 57 } 58 inline int get_max(int u,int op){ 59 if(op==1){ 60 while(!D[u].empty() && !A[u].empty() && D[u].top()==A[u].top()){ 61 D[u].pop();A[u].pop(); 62 } 63 if(!A[u].empty()) return A[u].top(); 64 else return -INF; 65 } 66 else{ 67 while(!E[u].empty() && !B[u].empty() && B[u].top()==E[u].top()){ 68 B[u].pop();E[u].pop(); 69 } 70 if(!B[u].empty()) return B[u].top(); 71 else return -INF; 72 } 73 } 74 inline int get_second(int u,int op){ 75 int ret; 76 if(op==1){ 77 int t=A[u].top();A[u].pop(); 78 ret=get_max(u,1); 79 A[u].push(t); 80 return ret; 81 } 82 else{ 83 int t=B[u].top();B[u].pop(); 84 ret=get_max(u,2); 85 B[u].push(t); 86 return ret; 87 } 88 } 89 void build_work_tree(int u){ 90 vist[u]=1; 91 A[u].push(0); 92 Anum[u]++; 93 dp[u]=-INF; 94 int t,vv; 95 t=indexx[u]; 96 while(t){ 97 vv=edge[t].v; 98 if(!vist[vv]){ 99 sum=siz[vv];rt=0; 100 find_rt(vv,u); 101 fa[rt]=u; 102 basic_dfs(u,rt,vv,0,edge[t].q); 103 if(!A[u].empty()) dp[u]=max(dp[u],get_max(u,1)+get_max(rt,2)); 104 A[u].push(B[rt].top());Anum[u]++; 105 build_work_tree(rt); 106 } 107 t=edge[t].nxt; 108 } 109 A[n+1].push(dp[u]); 110 } 111 void solve(int x){ 112 sit[x]^=1; 113 D[n+1].push(dp[x]); 114 if(sit[x]){ 115 A[x].push(0); 116 Anum[x]++; 117 } 118 else{ 119 D[x].push(0); 120 Anum[x]--; 121 } 122 if(Anum[x]==0 || get_max(x,1)<-10000000) dp[x]=-INF; 123 else if(Anum[x]==1 || get_second(x,1)<-10000000) dp[x]=0; 124 else dp[x]=get_max(x,1)+get_second(x,1); 125 A[n+1].push(dp[x]); 126 int u=x,t=0; 127 while(fa[u]){ 128 int f=fa[u]; 129 if(Bnum[u]==0) D[fa[u]].push(-INF); 130 else if(Bnum[u]==1) D[fa[u]].push(get_max(u,2)); 131 else D[fa[u]].push(get_max(u,2)); 132 if(sit[x]){ 133 B[u].push(dist[x][t]); 134 Bnum[u]++; 135 } 136 else{ 137 E[u].push(dist[x][t]); 138 Bnum[u]--; 139 } 140 if(Bnum[u]==0) A[fa[u]].push(-INF); 141 else if(Bnum[u]==1) A[fa[u]].push(get_max(u,2)); 142 else A[fa[u]].push(get_max(u,2)); 143 D[n+1].push(dp[fa[u]]); 144 if(get_max(fa[u],1)<-10000000) dp[fa[u]]=-INF; 145 else if(get_second(fa[u],1)<-10000000) dp[fa[u]]=0; 146 else dp[fa[u]]=get_max(fa[u],1)+get_second(fa[u],1); 147 A[n+1].push(dp[fa[u]]); 148 u=fa[u];t++; 149 } 150 } 151 int query(int x){ 152 return get_max(n+1,1); 153 } 154 signed main(){ 155 // freopen("a.txt","r",stdin); 156 int x,y,z; 157 n=read(); 158 for(int i=1;i<n;i++){ 159 x=read(); 160 y=read(); 161 z=read(); 162 addedge(x,y,z); 163 addedge(y,x,z); 164 sit[i]=1; 165 } 166 sit[n]=1; 167 rt=0;f[0]=INF;sum=n; 168 find_rt(1,0); 169 build_work_tree(rt); 170 for(int i=1;i<=n;i++){ 171 int len=dist[i].size(); 172 for(int j=0;j<len;j++){ 173 if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]); 174 else break; 175 } 176 } 177 scanf("%d",&m); 178 for(int i=1;i<=m;++i){ 179 scanf("%s",s); 180 if(s[0]=='C'){ 181 x=read(); 182 solve(x); 183 } 184 else{ 185 ans=query(x); 186 if(ans<-10000000) printf("They have disappeared.\n"); 187 else printf("%d\n",ans); 188 } 189 } 190 return 0; 191 }
題意:
求一棵帶點權邊權的樹上的帶權重心,以及重心的重量。
題解:
首先咱們要會給定點求值,這一步須要動態點分。
平常建點分樹,而後$dfs$出到各級父親的距離。
對於每一個點記錄點權和$Sum[x]$,本棵子樹內的帶權重量$All[x]$,以及對於父親的貢獻$Up[x]$。
而後查詢單點的值的時候從下到上枚舉父親,而後父親表明的那棵子樹中包括本身所在小子樹和其餘部分。
本身所在的小子樹的貢獻已經算完了,因此只須要計算其餘部分就能夠。
那麼發現其餘部分的值能夠先用$All[fa]-Up[son]$計算出最初根的,在加上因爲換根致使的$dist[st][cnt]*(Sum[fa]-Sum[son])$這樣就統計完了。
而後詢問重心的時候就是一個在樹上二分(若干分)的過程。每次看往哪一個兒子走更優就往那棵子樹走。有點像快遞員這道題的感受
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cstdio> 5 #include <vector> 6 #define N 200011 7 #define INF 0x3f3f3f3f 8 #define int long long 9 using namespace std; 10 struct apple{ 11 int v,q,nxt,root; 12 }edge[N*4]; 13 int indexx[N],vist[N],siz[N],f[N],sum,Sum[N],Up[N],All[N],rt,Root,n,m,tot,fa[N],ans; 14 vector<int> dist[N]; 15 void addedge(int x,int y,int z){ 16 edge[++tot].v=y; 17 edge[tot].q=z; 18 edge[tot].nxt=indexx[x]; 19 indexx[x]=tot; 20 } 21 void find_rt(int u,int fa){ 22 int t,vv; 23 siz[u]=1; 24 f[u]=0; 25 t=indexx[u]; 26 while(t){ 27 vv=edge[t].v; 28 if(vv!=fa && !vist[vv]){ 29 find_rt(vv,u); 30 siz[u]+=siz[vv]; 31 f[u]=max(f[u],siz[vv]); 32 } 33 t=edge[t].nxt; 34 } 35 f[u]=max(f[u],sum-siz[u]); 36 if(f[u]<f[rt]) rt=u; 37 } 38 void dfs(int u,int f,int dep){ 39 dist[u].push_back(dep); 40 int t,vv,qq; 41 t=indexx[u]; 42 while(t){ 43 vv=edge[t].v; 44 qq=edge[t].q; 45 if(!vist[vv] && vv!=f){ 46 dfs(vv,u,dep+qq); 47 } 48 t=edge[t].nxt; 49 } 50 } 51 void build_work_tree(int u){ 52 dfs(u,0,0); 53 int t,vv; 54 vist[u]=1; 55 t=indexx[u]; 56 while(t){ 57 vv=edge[t].v; 58 if(!vist[vv]){ 59 rt=0;sum=siz[vv]; 60 find_rt(vv,0); 61 edge[t].root=rt; 62 // no[rt]=++cnt; 63 fa[rt]=u; 64 build_work_tree(rt); 65 } 66 t=edge[t].nxt; 67 } 68 } 69 void change(int x,int y){ 70 int u=x,cnt=1,vv=x; 71 Sum[x]+=y; 72 x=fa[x]; 73 while(x){ 74 Sum[x]+=y; 75 Up[vv]+=y*dist[u][cnt]; 76 All[x]+=y*dist[u][cnt]; 77 vv=x; 78 x=fa[x]; 79 cnt++; 80 } 81 } 82 int ask(int x){ 83 int u=x,cnt=1,ret=0,vv=x; 84 ret+=All[x]; 85 x=fa[x]; 86 while(x){ 87 ret+=(Sum[x]-Sum[vv])*dist[u][cnt]+All[x]-Up[vv]; 88 vv=x; 89 x=fa[x]; 90 cnt++; 91 } 92 return ret; 93 } 94 void query(int x){ 95 vist[x]=1; 96 int t,vv,minn,nxt; 97 minn=ask(x);nxt=x; 98 t=indexx[x]; 99 while(t){ 100 vv=edge[t].v; 101 if(!vist[vv]){ 102 int temp=ask(vv); 103 if(temp<minn){ 104 minn=temp; 105 nxt=edge[t].root; 106 } 107 } 108 t=edge[t].nxt; 109 } 110 if(nxt==x){ 111 ans=minn; 112 vist[x]=0; 113 return; 114 } 115 else query(nxt); 116 vist[x]=0; 117 } 118 signed main(){ 119 freopen("a.txt","r",stdin); 120 int x,y,z; 121 scanf("%lld%lld",&n,&m); 122 for(int i=1;i<n;i++){ 123 scanf("%lld%lld%lld",&x,&y,&z); 124 addedge(x,y,z); 125 addedge(y,x,z); 126 } 127 f[0]=INF;sum=n; 128 find_rt(1,0); 129 Root=rt; 130 build_work_tree(rt); 131 for(int i=1;i<=n;i++){ 132 int len=dist[i].size(); 133 for(int j=0;j<len;j++){ 134 if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]); 135 else break; 136 } 137 } 138 memset(vist,0,sizeof(vist)); 139 for(int i=1;i<=m;i++){ 140 scanf("%lld%lld",&x,&y); 141 change(x,y); 142 query(Root); 143 printf("%lld\n",ans); 144 } 145 return 0; 146 }
題意:
給出一棵n個點的樹,每一個點有一個點權。
如今有q次操做,每次操做是修改一個點的點權或指定一個點,詢問以這個點爲根時每棵子樹點權和的平方和。
題解:
不愧是NOI rk1 ,zzq的題真的很棒。
若是查詢點權和的話很好搞,就變成了幻想鄉戰略遊戲。
而後發現有一個很小清新的結論(證實見zzq的洛谷題解吧):
$\sum_{i=1}^n s_i(w-s_i)$
不管根是啥都是一個定值。
因而就結束了。只要在最開始的根算出上述那堆東西而後就能夠再套幻想鄉戰略遊戲就好了。
真心好題。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <vector> 5 #define N 400011 6 #define int long long 7 #define INF 0x7f7f7f7f 8 using namespace std; 9 struct apple{ 10 int v,nxt; 11 }edge[N*4]; 12 int indexx[N],tot,f[N],siz[N],sum,n,m,rt,Sum[N],sigma,All[N],Up[N],val[N],vist[N],fa[N],W,ans; 13 vector<int> dist[N]; 14 void addedge(int x,int y){ 15 edge[++tot].v=y; 16 edge[tot].nxt=indexx[x]; 17 indexx[x]=tot; 18 } 19 void find_rt(int u,int fa){ 20 int t,vv; 21 siz[u]=1; 22 f[u]=0; 23 t=indexx[u]; 24 while(t){ 25 vv=edge[t].v; 26 if(vv!=fa && !vist[vv]){ 27 find_rt(vv,u); 28 siz[u]+=siz[vv]; 29 f[u]=max(f[u],siz[vv]); 30 } 31 t=edge[t].nxt; 32 } 33 f[u]=max(f[u],sum-siz[u]); 34 if(f[u]<f[rt]) rt=u; 35 } 36 void dfs(int anc,int u,int fa,int dep,int op){ 37 if(op==0) dist[u].push_back(dep); 38 if(op==0){ 39 All[anc]+=dep*val[u]; 40 Sum[anc]+=val[u]; 41 } 42 else Up[anc]+=dist[u][dist[u].size()-1]*val[u]; 43 int t,vv; 44 t=indexx[u]; 45 while(t){ 46 vv=edge[t].v; 47 if(!vist[vv] && vv!=fa){ 48 dfs(anc,vv,u,dep+1,op); 49 } 50 t=edge[t].nxt; 51 } 52 } 53 void work(int u){ 54 dfs(u,u,0,0,0); 55 vist[u]=1; 56 int t,vv; 57 t=indexx[u]; 58 while(t){ 59 vv=edge[t].v; 60 if(!vist[vv]){ 61 sum=siz[vv];rt=0; 62 find_rt(vv,0); 63 fa[rt]=u; 64 dfs(rt,rt,0,0,1); 65 work(rt); 66 } 67 t=edge[t].nxt; 68 } 69 } 70 void DP(int u,int fa){ 71 int t,vv; 72 siz[u]=val[u]; 73 t=indexx[u]; 74 while(t){ 75 vv=edge[t].v; 76 if(vv!=fa){ 77 DP(vv,u); 78 siz[u]+=siz[vv]; 79 } 80 t=edge[t].nxt; 81 } 82 W+=siz[u]*(sigma-siz[u]); 83 } 84 int query(int x){ 85 int ret=0,u=x,temp,cnt=1; 86 ret+=All[x]; 87 while(fa[x]){ 88 ret-=Up[x]; 89 ret+=All[fa[x]]; 90 ret+=(dist[u][cnt])*(Sum[fa[x]]-Sum[x]); 91 x=fa[x]; 92 cnt++; 93 } 94 return ret; 95 } 96 void change(int x,int y){ 97 W-=val[x]*(query(x)); 98 int u=x,cnt=1; 99 Sum[u]+=y-val[x]; 100 // All[u]+=y-val[x]; 101 while(fa[x]){ 102 Sum[fa[x]]+=y-val[u]; 103 All[fa[x]]+=(y-val[u])*dist[u][cnt]; 104 Up[x]+=(y-val[u])*dist[u][cnt]; 105 cnt++; 106 x=fa[x]; 107 } 108 sigma+=y-val[u]; 109 val[u]=y; 110 W+=val[u]*(query(u)); 111 } 112 void ask(int x){ 113 ans=sigma*(query(x)+sigma)-W; 114 printf("%lld\n",ans); 115 } 116 signed main(){ 117 freopen("a.txt","r",stdin); 118 int x,y,op; 119 scanf("%lld%lld",&n,&m); 120 for(int i=1;i<n;i++){ 121 scanf("%lld%lld",&x,&y); 122 addedge(x,y); 123 addedge(y,x); 124 } 125 for(int i=1;i<=n;i++){ 126 scanf("%lld",&val[i]); 127 sigma+=val[i]; 128 } 129 rt=0;f[0]=INF;sum=n; 130 find_rt(1,0); 131 work(rt); 132 DP(1,0); 133 for(int i=1;i<=n;i++){ 134 int len=dist[i].size(); 135 for(int j=0;j<len;j++){ 136 if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]); 137 else break; 138 } 139 } 140 for(int i=1;i<=m;i++){ 141 scanf("%lld",&op); 142 if(op==1){ 143 scanf("%lld%lld",&x,&y); 144 change(x,y); 145 } 146 else{ 147 scanf("%lld",&x); 148 ask(x); 149 } 150 } 151 return 0; 152 }