洛谷P3563 POI Polarization
这道题有结论啊啊啊
但是不会证啊qaq
最小就是尽量让少的点能够相互到达,每层的边都与上一层的边反向
最大要找到树的重心(这都能写错),然后对于每个子树,尽量让根连出的子树大小等于连到根的子树大小
也就是尽量取 n/2
转化成了0/1背包问题
套路:
对于 ∑a ≤ n
observation: 不同的数值不超过 O(√n)
多重背包,这样有 O(√n log n) 个数字从小到大,有一个数值超过了 2 就合并,这样有 O(√n) 个数字 (二进制优化)
分治 FFT,但实践中不快(这又是啥啊)
写这道题主要是练习一下 bitset 优化可行性背包,还有这个套路
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<cmath> #include<bitset> using namespace std; typedef long long ll; const int maxn = 250100; const int INF = 1e9+7; int n,m; ll ans; int c[maxn]; bitset<maxn> f; int h[maxn],size; struct E{ int to,next; }e[maxn<<1]; void add(int u,int v){ e[++size].to=v; e[size].next=h[u]; h[u]=size; } int rt; int sz[maxn],fa[maxn],son[maxn],maxs; void getroot(int u,int par){ sz[u]=1; son[u]=0; for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(v==par) continue; getroot(v,u); sz[u]+=sz[v]; son[u]=max(son[u],sz[v]); } son[u]=max(son[u],n-sz[u]); if(maxs>son[u]){ maxs=son[u]; rt=u; } } ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;} int main(){ memset(h,-1,sizeof(h)); n=read(); int u,v; for(int i=1;i<n;i++){ u=read(),v=read(); add(u,v),add(v,u); } maxs=INF; getroot(1,0); getroot(rt,0); for(int i=1;i<=n;i++) ans+=sz[i]-1; for(int i=h[rt];i!=-1;i=e[i].next){ ++c[sz[e[i].to]]; } for(int i=1;i<=n;i++){ while(c[i]>2){ c[i]-=2; ++c[i<<1]; } } f[0]=1; for(int i=1;i<=n;i++){ while(c[i]--){ f|=f<<i; } } int x; for(x=n>>1;x;x--) if(f[x]) break; printf("%d %lld\n",n-1,1ll*x*(n-1-x)+ans); return 0; }