BZOJ3425 : Poi2013 Polarization
最小值肯定是把树看作二分图,此时答案为$n-1$。
最大值一定是选取重心为根,任意一个子树要么全部指向根,要么全部背离根,这样可以制造最大的星型图。
统计出每个子树的大小后做01背包,如果小于$\sqrt{n}$,那么二进制拆分,否则这种子树不超过$\sqrt{n}$个,直接DP即可。
用bitset优化转移,时间复杂度$O(\frac{n\sqrt{n}}{32})$。
#include<cstdio> #include<bitset> #include<algorithm> #define N 250010 using namespace std; int n,m,i,j,k,x,y,g[N],v[N<<1],nxt[N<<1],ed,son[N],f[N],S,cnt[N],q[N],t; bitset<N>dp;long long ans,sum; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void findroot(int x,int y){ son[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=y){ findroot(v[i],x); son[x]+=son[v[i]]; if(son[v[i]]>f[x])f[x]=son[v[i]]; } if(n-son[x]>f[x])f[x]=n-son[x]; if(f[x]<f[S]||!S)S=x; } void dfs(int x,int y){ son[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x),son[x]+=son[v[i]]; if(y==S)if(son[x]<=m)cnt[son[x]]++;else q[++t]=son[x]; sum+=son[x]-1; } int main(){ read(n); for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x); findroot(1,0); while(m*m<n)m++; dfs(S,0); dp[0]=1; while(t)dp|=dp<<q[t--]; for(i=1;i<=m;i++)for(j=cnt[i],k=1;j;j-=k,k<<=1){ if(j<=k){dp|=dp<<i*j;break;} dp|=dp<<i*k; } for(i=0;i<n;i++)if(dp[i])ans=max(ans,1LL*i*(n-i-1)); return printf("%d %lld",n-1,ans+sum),0; }