[zjoi2015]幻想乡战略游戏
前言
略略略
題目相關
鏈接
題目大意
給出一棵樹,每次修改一個點的權值,維護一個帶權重心
啥是帶權重心?
設點iii的值為ViV_iVi?我們要選一個點uuu,每個點對應一個值:
∑v=1nVv?dis(u,v)\sum_{v=1}^n V_v*dis(u,v)v=1∑n?Vv??dis(u,v)
我們要選一個uuu使得這個值最小
輸出最小的值
數據范圍
n,q≤105n,q\le10^5n,q≤105,每個點的度數不多于20,時限6s
題解
動態點分治(點分樹)
很久之前學了點分樹,但一直都沒做什么題
我們考慮這樣一件事
假如我們現在選了一個點uuu,那么只有它向帶權重心的方向vvv移動時答案才會變優(證明很顯然,帶權重心方向的點權和比另外一邊的點權大,這樣的話e*點權只差就是正的)
我們考慮最裸的情況,每次修改后,我們從根節點開始,如果其往某個子樹移動答案會變優(設sizeisize_isizei?為iii的子樹大小,那么這個條件是eu,v?(sizeu?sizev?sizev<0e_{u,v}*(size_u-size_v-size_v<0eu,v??(sizeu??sizev??sizev?<0,容易發現這樣的兒子對多只有一個),那么說明帶權重心在那個子樹中,那就把當前答案往那個子樹移
我們發現這個復雜度可以被卡到O(n2)\mathcal O(n^2)O(n2),因為往兒子走可能會走很多次
考慮點分治之后建出點分樹,我們在跳子樹的時候跳到那個子樹的重心(注意與帶權重心區分)即可,容易發現,由于點分樹樹高logn,所以這個跳子樹的操作使得復雜度變為O(nlogn)\mathcal O(nlogn)O(nlogn)
現在要快速維護每個點的答案(當然是推過來而不是直接求)
我們維護每個分治子樹權值和sizeusize_usizeu?(uuu為分治重心),每個分治子樹到重心的貢獻值gug_ugu?,每個節點子樹到該子樹的lca的父親的貢獻值fuf_ufu?
怎么維護呢?即對于一次修改怎么維護答案
我們發現,直接跳點分樹即可,用O(1)\mathcal O(1)O(1)求距離的話,單次修改的復雜度是O(logn)\mathcal O(logn)O(logn)的
再維護一個全局點權和SIZESIZESIZE
我們現在相當于對于當前答案的每條出邊,并且詢問這條邊劃分出的子樹sizesizesize是否大于全局的一半,如果是就走過去,直到找到答案點,最終計算答案
我們發現子樹size和答案并不好維護
考慮記錄歷史走過的每一個點分重心,這樣我們可以做到單次lognlognlogn的詢問
我們發現一個點的度數總共只有202020個,直接枚舉出邊即可
綜上,復雜度O(20nlog2n)\mathcal O(20nlog^2n)O(20nlog2n)
不是很滿,但是復雜度感覺挺高的
代碼
#include<cstdio> #include<cctype> #include<algorithm> #include<vector> namespace fast_IO {const int IN_LEN=1000000,OUT_LEN=1000000;char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}inline void flush(){fwrite(obuf,1,oh-obuf,stdout);} } using namespace fast_IO; #define getchar() getchar_() #define putchar(x) putchar_((x)) #define rg register typedef long long ll; template <typename T> inline T max(const T a,const T b){return a>b?a:b;} template <typename T> inline T min(const T a,const T b){return a<b?a:b;} template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;} template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;} template <typename T> inline T abs(const T a){return a>0?a:-a;} template <typename T> inline void Swap(T&a,T&b){T c=a;a=b;b=c;} template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);} template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;} template <typename T> inline T square(const T x){return x*x;}; template <typename T> inline void read(T&x) {char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x; } template <typename T> inline void printe(const T x) {if(x>=10)printe(x/10);putchar(x%10+'0'); } template <typename T> inline void print(const T x) {if(x<0)putchar('-'),printe(-x);else printe(x); } const int maxn=100001,maxm=200001; int n,Q; int head[maxn],nxt[maxm],tow[maxm],vau[maxm],tmp; inline void addb(const int u,const int v,const int w) {tmp++;nxt[tmp]=head[u];head[u]=tmp;tow[tmp]=v;vau[tmp]=w; } int minn,root,SIZE,son[maxn]; bool vis[maxn]; void getroot(const int u,const int fa) {son[u]=1;int maxx=0;for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(v!=fa&&!vis[v]){getroot(v,u);son[u]+=son[v];maxd(maxx,son[v]);}}maxd(maxx,SIZE-son[u]);if(maxx<minn)minn=maxx,root=u; } int ROOT,F[maxn]; std::vector<int>E[maxn]; int fr[maxn]; void solve(const int u,const int SZ,const int SON) {vis[u]=1;for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(!vis[v]){minn=0x7fffffff,SIZE=son[v];if(SIZE>SON)SIZE=SZ-SON;getroot(v,u);E[u].push_back(root);F[root]=u,fr[root]=v;solve(root,SIZE,son[root]);}} } int rmq[maxm][21],top,fst[maxn]; ll dep[maxn]; void dfs(const int u,const int fa) {rmq[++top][0]=u;fst[u]=top;for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(v!=fa)dep[v]=dep[u]+vau[i],dfs(v,u),rmq[++top][0]=u;} } int bit[21],log_[3000000]; int MIN(const int u,const int v){return dep[u]<dep[v]?u:v;} int lca(const int u,const int v) {const int A=min(fst[u],fst[v]),B=max(fst[u],fst[v]);return MIN(rmq[A][log_[B-A+1]],rmq[B-bit[log_[B-A+1]]+1][log_[B-A+1]]); } ll dis(const int u,const int v) {return dep[u]+dep[v]-(dep[lca(u,v)]<<1); } ll g[maxn],f[maxn],size[maxn]; ll vvv[maxn]; int stack[21],tot;ll sz[21]; ll calc(const int l,const int r) {ll res=0;for(rg int i=1;i<=tot;i++)if(dis(stack[i],l)>dis(stack[i],r))res+=sz[i];return res; } ll Res(const int p) {ll res=g[p];stack[++tot]=p;for(rg int i=1;i<tot;i++)res+=g[stack[i]]-f[stack[i+1]]+(size[stack[i]]-size[stack[i+1]])*dis(stack[i],p);return res; } int main() {bit[0]=1;for(rg int i=1;i<=20;i++)bit[i]=bit[i-1]<<1;for(rg int i=1;i<=20;i++)for(rg int j=bit[i];j<(bit[i]<<1);j++)log_[j]=i;read(n),read(Q);for(rg int i=1;i<n;i++){int u,v,w;read(u),read(v),read(w);addb(u,v,w),addb(v,u,w);}minn=0x7fffffff,SIZE=n,getroot(1,1);ROOT=root;solve(root,SIZE,son[root]);dfs(1,1);for(rg int i=1;i<=20;i++)for(rg int j=1;j+bit[i]-1<=top;j++)rmq[j][i]=MIN(rmq[j][i-1],rmq[j+bit[i-1]][i-1]);SIZE=0;for(rg int i=1;i<=Q;i++){int p,O,more;read(p),O=p,read(more),vvv[p]+=more;size[p]+=more,SIZE+=more;while(p!=ROOT){const ll R=dis(O,F[p]);f[p]+=R*more,g[F[p]]+=R*more;p=F[p];size[p]+=more;}p=ROOT;tot=0;while(1){bool fla=1;for(unsigned int j=0;j<E[p].size();j++){const int v=E[p][j];const ll SZ=calc(p,fr[v])+size[v];if(SZ*2>SIZE){fla=0;stack[++tot]=p,sz[tot]=size[p]-size[v];p=v;break;}}if(fla)break;}print(Res(p)),putchar('\n');}return flush(),0; }總結
對點分樹的理解程度++
感覺自己還是做題太少了
總結
以上是生活随笔為你收集整理的[zjoi2015]幻想乡战略游戏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数列分块入门(套题)(loj6277,l
- 下一篇: 长链剖分:O(nlogn)预处理O(1)