1. 程式人生 > 其它 >CF1098C Construct a tree

CF1098C Construct a tree

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;
}