CF1098C Construct a tree
阿新 • • 發佈:2021-09-22
Solution
首先,我們考慮二分答案,先算出這個分支系數最小是多少,但難點就在於如何判斷,首先我們知道,如果不考慮分支系數,$ s $ 的最小值為 $ 2\times n -1 $ ,最大值為 $ \frac{n\times(n-1) }{2} $ ,而前者的分支系數為 $ n-1 $ 後者的分支系數為 $ 1 $,一種是菊花圖,一種是鏈,那這種構造方式是不是能給我們一點啟發,就是我們可不可以算出在分支系數為 $ a $ 時,他的 $ s $ 的上限與下限的構造方案是不是一成不變的,而當我們求出了最小的分支系數過後,我們就可以開始真正的構造了。
我們首先可以發現,題目中的要求可以轉化為 $ \sum_{i=1}^n dep_i=s $ ,也許你會問這樣子轉化一下有什麼用處,但我們發現,如果題目中的要求轉化成了這樣,我們只需要更換一個點的深度就可以改變他整棵樹的價值,而且可以做到,就是單純 $ +1 $ ,但是我們發現,這樣子是會 $ T $ 的,因為 $ s $ 的範圍在 $ (0,10^{10}] $ ,那我們就要加速。我們考慮最左邊的那條鏈為最終鏈,那麼對於一個點,他改變一定是改變到最左邊那條鏈,而改變的上限就是 $ sum-dep[now]+1 $ ,而 $ sum $ 就是那條最終鏈的長度。
Code
#include<bits/stdc++.h> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(!isdigit(ch)&&ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return x*f; } long long pow(long long x,long long y,long long p) { long long ans=1; for (;y;y>>=1,x=x*x % p) if (y&1) ans=ans*x % p; return ans; } long long up,U,ww,aa[1001010],fre[1001010],ls[1001010],nw,id,deg[1001010],gg,j,dfn[1001010],di,fat[1001010],n,i,idd,dfnn[1001010],K,l,r,mid,ans,down,bb,dif,now,ed[1001010],sum,dep[1001010],e[1001010]; vector<int> edge[1001010]; bool exist[1001010]; void sc(int x,int fa) { dep[x]=1; for (int i=0;i<edge[x].size();i++) { int y=edge[x][i]; if (dep[y]==0) { sc(y,x);dep[x]+=dep[y]; } } } long long calc(long long mid) { for (i=1;i<=n;i++) edge[i].clear(),dep[i]=0; for (i=1;i<=n;i++) fre[i]=0; int nw=1; for (i=2;i<=n;i++) { edge[nw].push_back(i); fre[nw]++; if (fre[nw]==mid) nw++; } sc(1,0); long long ans=0; for (i=1;i<=n;i++) ans+=dep[i]; return ans; } void sc_build(int x,int fa) { id++;dfnn[id]=x; for (int i=0;i<edge[x].size();i++) if (edge[x][i]!=fa) { dep[edge[x][i]]=dep[x]+1; sc_build(edge[x][i],x); } } int main() { //ios::sync_with_stdio(0);cin.tie();cout.tie(); n=read();K=read(); if ((K<2*n-1)||(K>n*(n+1)/2)) { puts("No");return 0; } puts("Yes"); l=1;r=n-1; while (l<=r) { mid=(l+r)/2; if (calc(mid)<=K) { ans=mid;r=mid-1; } else l=mid+1; } down=calc(ans);dif=K-down;bb=1; // cout<<dif<<endl; for (now=0;now<n;now+=bb,bb*=ans) sum++,ed[sum]=now+1,exist[now+1]=true; //cout<<"fuck"<<sum<<endl; for (i=1;i<=n;i++) edge[i].clear(); for (i=1;i<=n;i++) fre[i]=0,dep[i]=0; dep[1]=1; nw=1; for (i=2;i<=n;i++) { edge[nw].push_back(i);deg[nw]++;deg[i]++; fat[i]=nw; fre[nw]++; if (fre[nw]==ans) nw++; } sc_build(1,0);idd=sum; for (i=1;i<=n;i++) if (exist[i]==false) { gg++;up=dfnn[i];for (j=dfnn[i];j;j=fat[j]) up=j; U=max(U,dep[i]); if ((gg>1)&&(up==2)) { ww++;aa[ww]=i; } } for (i=U;i>=1;i--) for (j=1;j<=ww;j++) if (dep[aa[j]]==i) { idd++;dfn[idd]=aa[j];exist[aa[j]]=true; } ww=0; for (i=1;i<=n;i++) if (exist[i]==false) { gg++;up=dfnn[i];for (j=dfnn[i];j;j=fat[j]) up=j; U=max(U,dep[i]); if ((gg>1)&&(up!=2)) { ww++;aa[ww]=i; } } for (i=U;i>=1;i--) for (j=1;j<=ww;j++) if (dep[aa[j]]==i) { idd++;dfn[idd]=aa[j]; } //sum:原先鏈的長度。 //cout<<fat[2]<<endl; if (dif!=0) { for (i=sum+1;i<=n;i++) { now=dfn[i];//cout<<now<<endl; if (dif>sum-dep[now]+1) { dif-=(sum-dep[now]+1); fat[now]=ed[sum]; sum++;ed[sum]=now; } else { di=dep[now]+dif-1; //cout<<ed[di]<<endl; dif=0; fat[now]=ed[di];break; } } } //cout<<"------"<<dif<<"------"<<endl; for (i=2;i<=n;i++) printf("%d ",fat[i]); return 0; }