• 【BZOJ3425】Poi2013 Polarization 猜结论+DP


    【BZOJ3425】Poi2013 Polarization

    Description

    给定一棵树,可以对每条边定向成一个有向图,这张有向图的可达点对数为树上有路径从u到达v的点对(u,v)个数。求最小可达点对数和最大可达点对数

    n<=250000

    Sample Input

    4
    1 2
    1 3
    1 4

    Sample Output

    3 5

    题解:想了一晚上,怎么想怎么是个搭建双塔,结果看题解发现还真tm是搭建双塔。

    本题的结论有点神,不过很好猜,证明见Claris博客。

    第一问的答案一定是n-1,因为树是个二分图,所以我们将树黑白染色后所有边都从黑点指向白点即可。

    第二问的答案是什么呢?我们猜测一定是先选中一个点,然后一半的边都指向这个点,另一半的边从这个点指出去。而选择哪个点呢?显然是重心啦。然后我们将重心之外所有联通块的大小都拿出来,这就变成了搭建双塔问题。

    话说搭建双塔不是$O(n^2)$的吗?n=250000你逗我?然而正解很奇特。

    当联通块大小<sqrt(n)时,我们将所有这样的联通块合到一起跑分组背包;当联通块大小>sqrt(n)时,这样的联通块不超过sqrt(n)个,所以暴力DP即可。DP时用bitset维护,于是时间复杂度就变成了神奇的$O({n sqrt{n}over 32})$。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <bitset>
    #include <cmath>
    using namespace std;
    const int maxn=250010;
    typedef long long ll;
    int B,n,m,cnt,rt,mn;
    ll sum,ans;
    int to[maxn<<1],next[maxn<<1],head[maxn],siz[maxn],v[maxn],dep[maxn],s[maxn];
    bitset<maxn> f;
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
    	return ret*f;
    }
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    void dfs(int x)
    {
    	siz[x]=1,sum+=dep[x]-1;
    	int i,tmp=0;
    	for(i=head[x];i!=-1;i=next[i])	if(!dep[to[i]])
    		dep[to[i]]=dep[x]+1,dfs(to[i]),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
    	tmp=max(tmp,n-siz[x]);
    	if(tmp<mn)	mn=tmp,rt=x;
    }
    int main()
    {
    	n=rd(),B=int(sqrt(double(n))),mn=1<<30;
    	memset(head,-1,sizeof(head));
    	int i,j,a,b;
    	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
    	dep[1]=1,dfs(1),memset(dep,0,sizeof(dep));
    	sum=0,dep[rt]=1,dfs(rt);
    	for(i=head[rt];i!=-1;i=next[i])	v[++m]=siz[to[i]];
    	f[0]=1;
    	for(i=1;i<=m;i++)
    	{
    		if(v[i]<=B)	s[v[i]]++;
    		else	f=f|(f<<v[i]);
    	}
    	for(i=1;i<=B;i++)
    	{
    		for(j=1;j<=s[i];s[i]-=j,j<<=1)	f=f|(f<<(i*j));
    		if(s[i])	f=f|(f<<(i*s[i]));
    	}
    	for(i=0;i<=n;i++)	if(f[i])	ans=max(ans,sum+(ll)i*(n-1-i));
    	printf("%d %lld",n-1,ans);
    	return 0;
    }

     

  • 相关阅读:
    插件制作入门
    Hank老师推荐的一些博客
    高级iOS工程师的博客
    查看一个文件是否支持64位 方法 ,[symbol(s) not found for architecture x86_64]相关
    ssh-keygen的使用方法及配置authorized_keys两台linux机器相互认证
    使用git在两台机器间同步代码
    scp远程拷贝命令
    后台启动程序并重定向输出信息脚本
    automake 工具的使用
    minigui启动过程
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7787776.html
Copyright © 2020-2023  润新知