10.26模擬賽
預感最近的模擬賽都是CF重組場。
T1
考場思路:
純暴力模擬。
先求出所有點到\(0\)號點的路徑上經過的節點。
然後再轉化為兩個點之間的路徑(0號點到左端點和0號點到左端點減去0號點到lca),記錄哪些點在路徑上,對於每個路徑求出mex。
最後統計mex的貢獻。
瓶頸在於求解mex需要依賴於求出具體每條路徑經過了哪些節點。
時間複雜度應該在\(O(n^2logn)\)左右,應該可以拿30分,但是實際上掛到了5分。
正解思路:
一條路徑的mex為\(k\)的充要條件為:\([0,k-1]\)在這條路徑上都出現了,但是\(k\)沒有出現。
維護\(k\)在一條路徑上沒有出現是不好維護的,考慮容斥出來用\(mex\ge k\)
現在問題轉化為了求\(mex\)為\([0,n-1]\)的路徑條數。
考慮依次列舉每個點。
如果當前列舉的點是\(k\),\([0,k-1]\)都在一條鏈上,鏈上的端點為\(st\)和\(ed\)。
如果\(k\)也在這條鏈上,那麼它有可能成為鏈的端點。
如果\(st\)和\(ed\)的lca不為其中一個,說明此時鏈不被算在st或ed的子樹中(類似一下這種情況)。那麼路徑條數為\(siz[st]\times siz[ed]\)。
從st的子樹(不包含當前鏈)中的任一節點到ed的子樹(不包含當前鏈)中的任一節點都經過了當前鏈,這些路徑的mex一定大於等於\(k\)
。
如果\(st\)和\(ed\)的lca為其中一個,說明此時鏈被算在st或ed的其中一個子樹中(類似於以下這種情況)。
此時的路徑條數為\(siz[st]\times(n-siz[ed'])\)
如果\(k\)不在這條鏈上,那麼\([k,n]\)之間的\(mex\)一定為\([0,k-1]\)之間的數,而\(mex\)為\([0,k-1]\)的路徑數已經統計過了,此時就可以跳出了。
code
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long LL; int const maxe=400101; int const maxn=200101; int const Log=19; int t,n; struct Edge{ int nxt,to; }e[maxe]; int head[maxn],tot; int dep[maxn],f[maxn][Log+1]; int siz[maxn]; void addedge(int x,int y){ e[++tot].nxt=head[x]; head[x]=tot; e[tot].to=y; } void dfs(int x,int fa){ dep[x]=dep[fa]+1;siz[x]=1; f[x][0]=fa; for(int k=1;k<=Log;k++) f[x][k]=f[f[x][k-1]][k-1]; for(int i=head[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa) continue; dfs(y,x); siz[x]+=siz[y]; } } int get_lca(int x,int y){ if(dep[x]>dep[y]) swap(x,y); for(int i=Log;i>=0;i--) if(dep[f[y][i]]>=dep[x]) y=f[y][i]; if(x==y) return x; for(int i=Log;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int dist(int x,int y){ int lca=get_lca(x,y); return dep[x]+dep[y]-2*dep[lca]; } LL ans[maxn]; int main(){ scanf("%d",&t); while(t--){ memset(head,0,sizeof(head));tot=0; memset(ans,0ll,sizeof(ans)); scanf("%d",&n); for(int i=1;i<=n-1;i++){ int x,y; scanf("%d%d",&x,&y); addedge(x,y);addedge(y,x); } dfs(0,0); ans[0]=1ll*(n)*(n-1);ans[0]>>=1ll; for(int i=head[0];i;i=e[i].nxt){ int y=e[i].to; ans[1]+=1ll*siz[y]*(n-siz[y]); } ans[1]+=1ll*(siz[0]-1)*(n-siz[0]+1);ans[1]>>=1ll; int st=0,ed=0; for(int i=1;i<n;i++){ int len1=dist(st,ed),len2=dist(st,i)+dist(i,ed); if(len1!=len2){ if(len2-len1==2*dist(i,st)) st=i; else if(len2-len1==2*dist(i,ed)) ed=i; else break; } int lca=get_lca(st,ed); if((st!=lca)&&(lca!=ed)) ans[i+1]=1ll*siz[st]*siz[ed]; else{ int x=st,y=ed; if(dep[x]<dep[y])swap(x,y); ans[i+1]=1ll*siz[x]; for(int k=Log;k>=0;k--) if(dep[f[x][k]]>dep[y]) x=f[x][k]; ans[i+1]*=1ll*(n-siz[x]); } } for(int i=0;i<n;i++) ans[i]-=ans[i+1]; for(int i=0;i<=n;i++) printf("%lld ",ans[i]); puts(""); } return 0; }