P3233-[HNOI2014]世界树【虚树,倍增】
生活随笔
收集整理的這篇文章主要介紹了
P3233-[HNOI2014]世界树【虚树,倍增】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
題目鏈接:https://www.luogu.com.cn/problem/P3233
題目大意
nnn個點的一棵樹,mmm次選出一些點作為關鍵點。每個樹上的點會對最近的關鍵點做貢獻,求每個關鍵點的貢獻。
解題思路
顯然是虛樹,考慮如何在虛樹上求貢獻,我們發現難處理的是虛樹上每一條鏈的貢獻,因為這個鏈上代表了原樹上的多個點。我們可以先對于虛樹上每一個點求出一個nerxner_xnerx?表示距離它最近的關鍵點。
然后我們對于一條邊,我們可以計算出一個長度表示該邊代表的鏈這個長度一下都是屬于yyy的,上面的屬于xxx的。然后我們可以倍增求出原樹上的分界點來計算答案。
因為建虛樹和求答案的logloglog是分開來的,所以時間復雜度為O((n+∑k)log?n)O(\ (n+\sum k)\ \log n)O(?(n+∑k)?logn)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=300100,T=20; struct node{int to,next; }a[N*2]; int n,q,k,tot,cnt,ls[N],siz[N],dep[N],dfn[N]; int ner[N],ans[N],p[N],pos[N],s[N],f[N][T+1]; void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return; } bool cmp(int x,int y){return dfn[x]<dfn[y];} bool cMp(int x,int y){return pos[x]<pos[y];} int LCA(int x,int y){if(dep[x]>dep[y])swap(x,y);for(int i=T;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];if(x==y)return x;for(int i=T;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0]; } int gup(int x,int k){for(int i=T;i>=0;i--)if((k>>i)&1)x=f[x][i];return x; } void Add(int x){if(!cnt){s[++cnt]=x;return;}int lca=LCA(x,s[cnt]);while(cnt>1&&dep[s[cnt-1]]>dep[lca])addl(s[cnt-1],s[cnt]),cnt--;if(dep[s[cnt]]>dep[lca])addl(lca,s[cnt]),cnt--;if((!cnt)||(s[cnt]!=lca))s[++cnt]=lca;s[++cnt]=x;return; } void dfs1(int x,int fa){siz[x]=1;dep[x]=dep[fa]+1;dfn[x]=++cnt;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;f[y][0]=x;dfs1(y,x);siz[x]+=siz[y];}return; } int get_dis(int x,int y) {return dep[x]+dep[y]-2*dep[LCA(x,y)];} void dfs_son(int x,int fa){if(pos[x])ner[x]=x;else ner[x]=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to,w=dep[y]-dep[x];if(y==fa)continue;dfs_son(y,x);if(!ner[x]||dep[ner[y]]<dep[ner[x]]||(dep[ner[y]]==dep[ner[x]]&&ner[y]<ner[x]))ner[x]=ner[y];}return; } void dfs_father(int x,int fa){for(int i=ls[x];i;i=a[i].next){int y=a[i].to,w=dep[y]-dep[x];if(y==fa)continue;int zx=get_dis(x,ner[x]),zy=get_dis(y,ner[y]);if(!ner[y]||zy>zx+w||(zy==zx+w&&ner[x]<ner[y]))ner[y]=ner[x]; dfs_father(y,x);}return; } void solve(int x,int fa){ans[ner[x]]+=siz[x]; for(int i=ls[x];i;i=a[i].next){int y=a[i].to,w=dep[y]-dep[x]-1,gd=gup(y,w);if(y==fa)continue;if(ner[x]==ner[y])ans[ner[x]]+=siz[gd]-siz[y];else{int zx=get_dis(x,ner[x]),zy=get_dis(y,ner[y]);int k=(zx+zy+w)/2;if((zx+zy+w)&1)k+=ner[y]<ner[x];k-=zy;int pw=gup(y,k);if(k<=0)ans[ner[x]]+=siz[gd]-siz[y];else if(k>w)ans[ner[y]]+=siz[gd]-siz[y];else ans[ner[y]]+=siz[pw]-siz[y],ans[ner[x]]+=siz[gd]-siz[pw];}ans[ner[x]]-=siz[gd];solve(y,x);}ls[x]=0;return; } int main() {scanf("%d",&n);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}dfs1(1,1);for(int j=1;j<=T;j++)for(int i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];memset(ls,0,sizeof(ls));scanf("%d",&q);while(q--){scanf("%d",&k);tot=cnt=0;for(int i=1;i<=k;i++)scanf("%d",&p[i]),pos[p[i]]=i;sort(p+1,p+1+k,cmp);if(p[1]!=1)s[++cnt]=1;for(int i=1;i<=k;i++)Add(p[i]);while(cnt>1)addl(s[cnt-1],s[cnt]),cnt--;dfs_son(1,1);dfs_father(1,1);solve(1,1);sort(p+1,p+1+k,cMp);for(int i=1;i<=k;i++)printf("%d ",ans[p[i]]),ans[p[i]]=pos[p[i]]=0;putchar('\n');}return 0; }總結
以上是生活随笔為你收集整理的P3233-[HNOI2014]世界树【虚树,倍增】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑出现问题没人上门修如何找人上门维修电
- 下一篇: CSPNOIP2020总结