洛谷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;
}
原文地址:https://www.cnblogs.com/tuchen/p/10413007.html