luogu P3345 [ZJOI2015]幻想鄉戰略遊戲(點分樹)
題意自己看。。。
思路
沒想到今(昨)天刷著刷著點分治的水題,就刷出來了一個點分樹。。。
然後就瘋狂地找題解,代碼,最後終於把它給弄懂了。
點分樹——動態點分治,對於此題來說,我們發現設u為當前的補給站位置,v是它的一個兒子。同時設dis(i,j)為樹上i點到j點的距離。sumi為以i為跟的子樹中d(也就是軍隊數)的總量
我們把補給站從u轉移到v,答案的變化為dis(u,v)*(sumu-sumv)-dis(u,v)*sumv=dis(u,v)*(sumu-2*sumv)
所以當2*sumv>sumu把補給站轉移v會使答案變優,一直這樣操作下去,直到不管如何轉移都無法使答案變優,當前點就是答案。
我們對於每一次修改都維護一下sum然後默認根為補給站,然後像剛才一樣轉移補給站,就有了這個題的大體思路。
但這樣肯定會爆炸啊(每次只會轉移一步,復雜度和深度有關)
所以我們就可以用到點分樹了。
我們利用點分治的方法(找重心作為根節點,然後遞歸處理子樹)重新構建一顆樹,稱為分治樹。
舉個例子,比如說樣例
(左為原樹右為分治樹)
這樣我們就得到一顆深度為log的樹。
這樣我們轉移就不會因為深度太大而GG了。
但是,這樣一來的話因為樹的結構不一樣了,補給站的轉移就會很難。因為一個點的兒子在原樹中不一定和它直接相連(之後我們的補給站的轉移都是在分治樹上進行的)
但還是有辦法解決的。我們記錄這這些東西。dis(i,j)為i,j在原樹的距離。cnt[i]為分治樹中以i為根的子樹中有多少個d,sumdi
最後一個答案數組sumi,這個數組比較玄學,建議結合代碼理解,當i為當前選定補給站時suni是所有的dx*dis(x,i)
這個東西很好維護,我們預處理出每一個點在分治樹中的每一個祖先,修改時,一個一個跳祖先就行了。
我們現在討論從u轉移到它分治樹中的一個子節點v,v是原來這顆子樹中的重心但在原樹中並不一定和u之間相連,我們設原樹中直接和u相連的點是w
只要把v所在子樹之外的所有信息扔到w中然後遞歸就可以了。那怎麽維護w的信息呢?
我們記錄在v處記錄下w和w與u這條邊的權值,然後我們在cntw處加上cnt[u]-cnt[v],然後在sumw
然後我們在維護的時候在w分治樹的祖先的sum都加上sumw,最後答案就取sum[當前點]就行了。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<vector> 5 #include<cmath> 6 #include<algorithm> 7 using namespace std; 8 #define ll long long 9 typedef pair<ll,ll>p1; 10 typedef pair<ll,p1>p2; 11 const long long N=100100; 12 vector<p1> pre[N];//存分治樹祖先的編號,和原樹中到祖先的距離 13 vector<p2> ch[N];//存與分治樹的兒子,同一子樹中的原樹中的兒子,和連接原樹中的兒子的邊權 14 vector<p2> record;//回溯用 15 vector<ll> sumdist[N];//存分治樹中的兒子的sum 16 long long tot,head[N]; 17 long long size[N],g[N],vis[N],all,realroot,root,n,m; 18 long long sum[N],cnt[N]; 19 struct edge{ 20 long long to,nxt,w; 21 }e[N<<1]; 22 void add(long long u,long long v,long long w){ 23 tot++; 24 e[tot].nxt=head[u]; 25 e[tot].to=v; 26 e[tot].w=w; 27 head[u]=tot; 28 } 29 void get_root(long long u,long long fa){ 30 size[u]=1; 31 g[u]=0; 32 for(long long i=head[u];i;i=e[i].nxt){ 33 long long v=e[i].to; 34 if(vis[v]==1||v==fa)continue; 35 get_root(v,u); 36 size[u]+=size[v]; 37 g[u]=max(g[u],size[v]); 38 } 39 g[u]=max(g[u],all-size[u]); 40 if(g[u]<g[root])root=u; 41 } 42 void get_dis(long long u,long long fa,long long top,long long w){ 43 pre[u].push_back(p1(top,w)); 44 size[u]=1; 45 for(long long i=head[u];i;i=e[i].nxt){ 46 long long v=e[i].to; 47 if(vis[v]==1||v==fa)continue; 48 get_dis(v,u,top,w+e[i].w); 49 size[u]+=size[v]; 50 } 51 } 52 void work(long long u){ 53 vis[u]=1; 54 pre[u].push_back(p1(u,0)); 55 for(long long i=head[u];i;i=e[i].nxt){ 56 long long v=e[i].to; 57 if(vis[v]==1)continue; 58 get_dis(v,0,u,e[i].w); 59 root=0;all=size[v]; 60 get_root(v,0); 61 ch[u].push_back(p2(root,p1(v,e[i].w))); 62 work(root); 63 } 64 } 65 void update(long long x,ll c,ll cc){ 66 for(long long i=0;i<pre[x].size();++i){ 67 long long u=pre[x][i].first; 68 cnt[u]+=c; 69 sum[u]+=cc+c*pre[x][i].second; 70 if(i!=pre[x].size()-1){ 71 for(long long j=0;j<ch[u].size();j++) 72 if(ch[u][j].first==pre[x][i+1].first)sumdist[u][j]+=cc+c*pre[x][i].second; 73 } 74 } 75 } 76 ll query(){ 77 long long x=realroot; 78 long long mx; 79 record.clear(); 80 while(x){ 81 mx=0; 82 for(long long i=1;i<ch[x].size();i++){ 83 if(cnt[ch[x][i].first]>cnt[ch[x][mx].first])mx=i; 84 } 85 if(ch[x].size()==0||cnt[ch[x][mx].first]*2<=cnt[x]){ 86 ll ans=sum[x]; 87 for(long long i=0;i<record.size();i++){ 88 update(record[i].first,record[i].second.first,record[i].second.second); 89 } 90 return ans; 91 } 92 long long v=ch[x][mx].first; 93 record.push_back(p2(ch[x][mx].second.first,p1(-(cnt[x]-cnt[v]),-(sum[x]-sumdist[x][mx]+(cnt[x]-cnt[v])*ch[x][mx].second.second)))); 94 update(ch[x][mx].second.first,cnt[x]-cnt[v],sum[x]-sumdist[x][mx]+(cnt[x]-cnt[v])*ch[x][mx].second.second); 95 x=v; 96 } 97 } 98 int main(){ 99 scanf("%lld%lld",&n,&m); 100 for(long long i=1;i<=n-1;++i){ 101 long long u,v,w; 102 scanf("%lld%lld%lld",&u,&v,&w); 103 add(u,v,w); 104 add(v,u,w); 105 } 106 g[0]=n+10;root=0;all=n; 107 get_root(1,0);realroot=root; 108 work(root); 109 for(long long i=1;i<=n;++i){ 110 sumdist[i]=vector<ll>(ch[i].size(),0); 111 } 112 while(m--){ 113 long long x,y; 114 scanf("%lld%lld",&x,&y); 115 update(x,y,0); 116 printf("%lld\n",query()); 117 } 118 return 0; 119 }
luogu P3345 [ZJOI2015]幻想鄉戰略遊戲(點分樹)