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

  

posted @ 2016-03-17 21:10  Claris  阅读(664)  评论(0编辑  收藏  举报