1. 程式人生 > >BZOJ.3425.[POI2013]Polarization(DP 多重揹包)

BZOJ.3425.[POI2013]Polarization(DP 多重揹包)

BZOJ
洛谷

最小可到達點對數自然是把一條路徑上的邊不斷反向,也就是黑白染色後都由黑點指向白點。這樣答案就是\(n-1\)
最大可到達點對數,容易想到找一個點\(a\),然後將其子樹分為兩部分\(x,y\)\(x\)子樹所有邊全指向\(a\)\(a\)\(y\)子樹之間的邊全指向\(y\)。這樣答案就是\(sz[x]\times sz[y]\),要讓\(sz[x],sz[y]\)儘量相等。找重心就好了。

然後DP,求劃分重心兩部分子樹大小分別為\(x\)\(n-1-x\)是否可行。
\(f[i]\)表示一部分子樹\(sz\)和為\(i\)是否可行。轉移就是個可行性揹包,可以用\(bitset\)

優化到\(\frac{n^2}{w}\),但還是不夠。
對於\(size\geq\sqrt{n}\)的子樹,最多不會超過\(\sqrt{n}\)個,可以直接揹包轉移。
對於\(size<\sqrt{n}\)的子樹,根據\(size\)按多重揹包做,二進位制拆分,複雜度為\(O(\sqrt{n}\log n)\)
最終複雜度:\(O(\frac{n\sqrt{n}\log n}{w})\)

//13704kb   3020ms
#include <cmath>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=250005;

int n,Enum,H[N],nxt[N<<1],to[N<<1],sz[N],root,Max,cnt[N];
std::bitset<N> f;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
inline void AE(int u,int v)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void Get_root(int x,int f)
{
    int mx=0; sz[x]=1;
    for(int i=H[x],v; i; i=nxt[i])
        if((v=to[i])!=f)
        {
            Get_root(v,x), sz[x]+=sz[v];
            if(sz[v]>mx) mx=sz[v];
        }
    mx=std::max(mx,n-sz[x]);
    if(mx<Max) Max=mx, root=x;
}
void DFS(int x,int f)
{
    sz[x]=1;//重算一遍sz啊 想什麼呢 
    for(int i=H[x],v; i; i=nxt[i])
        if((v=to[i])!=f) DFS(v,x), sz[x]+=sz[v];
}

int main()
{
    n=read();
    for(int i=1; i<n; ++i) AE(read(),read());
    Max=1e9, Get_root(1,1), DFS(root,0);

    f[0]=1; const int lim=sqrt(n);
    for(int i=H[root]; i; i=nxt[i])
        if(sz[to[i]]<lim) ++cnt[sz[to[i]]];
        else f|=f<<sz[to[i]];
    for(int i=1; i<lim; ++i)
        for(int j=cnt[i],k=1; j; j-=k,k<<=1)
            if(j>k) f|=f<<i*k;
            else {f|=f<<i*j; break;}

    LL ans=0;
    for(int i=1; i<n; ++i) if(f[i]) ans=std::max(ans,1ll*i*(n-1-i));
    for(int i=1; i<=n; ++i) ans+=sz[i];
    printf("%d %lld\n",n-1,ans-n);

    return 0;
}