1. 程式人生 > 實用技巧 >題解 洛谷 P3563 【[POI2013]POL-Polarization】

題解 洛谷 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",n-1,ans+(ll)i*(n-i-1));
        break;
    }
    return 0;
}