BZOJ.3425.[POI2013]Polarization(DP 多重揹包)
阿新 • • 發佈:2018-11-11
最小可到達點對數自然是把一條路徑上的邊不斷反向,也就是黑白染色後都由黑點指向白點。這樣答案就是\(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\)
對於\(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; }