zoukankan      html  css  js  c++  java
  • 题解 洛谷 P3563 【[POI2013]POL-Polarization】

    先考虑最小值,因为树是二分图,所以可以进行黑白染色,将其分成左右部图,让左部图向右部图连边即可构造最小值,为 (n-1)

    对于最大值,最优情况一定是以一个点为中心,其各个子树内边的状态为要么该点能到达子树内的每个点,即外向,要么子树内的每个点能到达该点,即内向,且该点为树的重心。

    可以简单的进行证明。考虑边的状态让这棵树有了两个上面描述的中心,两个中心之间的路径上一半的边指向第一个中心,一半的边指向第二个中心。发现若这条路径边的方向只指向一个中心,答案会更优,所以就将这两个中心合并为一个,同理,若存在多个中心,合并为一个会更优。因为中心的各个子树的外向个数和内向个数会乘起来贡献给答案,所以中心选取树的重心可以更好的分配外向内向的子树大小。

    考虑将中心的各个子树分为外向和内向两个集合,两个集合大小越接近 (frac{n}{2}) 越优。可以通过二进制拆分来优化这个多重背包问题,因为只用考虑存在性,所以也采用 (bitset) 来优化。

    (code:)

    #include<bits/stdc++.h>
    #define maxn 500010
    using namespace std;
    typedef long long ll;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int n,root;
    ll ans;
    int siz[maxn],ma[maxn],cnt[maxn];
    bitset<maxn> s;
    struct edge
    {
        int to,nxt;
    }e[maxn];
    int head[maxn],edge_cnt;
    void add(int from,int to)
    {
        e[++edge_cnt]=(edge){to,head[from]};
        head[from]=edge_cnt;
    }
    void dfs(int x,int fa)
    {
        siz[x]=1;
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(y==fa) continue;
            dfs(y,x),siz[x]+=siz[y];
            ma[x]=max(ma[x],siz[y]);
        }
        ma[x]=max(ma[x],n-siz[x]);
        if(ma[x]<ma[root]) root=x;
    }
    int main()
    {
        read(n),ma[0]=n;
        for(int i=1;i<n;++i)
        {
            int x,y;
            read(x),read(y);
            add(x,y),add(y,x);
        }
        dfs(1,0),dfs(root,0);
        for(int i=1;i<=n;++i) ans+=siz[i]-1;
        for(int i=head[root];i;i=e[i].nxt) cnt[siz[e[i].to]]++;
        for(int i=1;i<=n;++i)
            while(cnt[i]>2)
                cnt[i]-=2,cnt[i*2]++;
        s[0]=1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=cnt[i];++j)
                s|=s<<i;
        for(int i=n/2;i;--i)
        {
            if(!s[i]) continue;
            printf("%d %lld
    ",n-1,ans+(ll)i*(n-i-1));
            break;
        }
        return 0;
    }
    
  • 相关阅读:
    Hibernate关于字段的属性设计
    Hibernate之增删查改常见错误
    Hibernate之实体类设计基本步骤
    Github开源之旅第二季-MarkDown
    8.Git命令(上)
    9.Git命令(下)
    7.Git Bash操作的四个坑(基本LINUX操作)
    Webserver-HTTP项目(深入理解HTTP协议)
    JAVA入门到精通-第94讲-山寨QQ项目8-好友在线提示
    JAVA入门到精通-第93讲-山寨QQ项目7-好友在线提示
  • 原文地址:https://www.cnblogs.com/lhm-/p/13399231.html
Copyright © 2011-2022 走看看