Codeforces Round 890 (Div. 2) E2. PermuTree (hard version)(根号分治+二进制优化多重背包+不定长bitset优化01背包)(nsqrt/w)

题目

n(n<=1e6)的一棵树,将[1,n]的某排列p分配到树上每个点的点权,

计点i的点权是ai,最大化(u,v)点对的数量,使得a[u]

思路来源

hanasaki竹子代码

心得

学了下开不定长的bitset的写法,可以通过模板函数递归的方式,

因为模板函数的参数是个const,但又支持传入这个参数

不断翻倍,就可以找到超过且和当前tot大小最接近的bitset的大小

templatevoid solve(){
    if(LEN<=tot){solve();return;}

}

solve<1>();

题解

每个子树u的直连儿子v,每个v有一个size,

在对lca决策的时候,将一部分放比lca小的值,另一部分放比lca大的值,

这样贡献就是小的个数*大的个数,相当于做一个背包,使得二者越接近越好

计u的子树size总和为tot,如果存在一个子树的size超过tot的一半,显然可以直接算

否则,每个子树的size都不超过一半,递归log层就到底了,

所以遍历[0,tot]找答案是可行的,这部分复杂度O(nlogn)

根号分治

将sz不超过sqrt(n)的物品计在数组里,

遍历小于根号的,从底往上推,

每个物品至少留一个,其余剩下的都可以推到二倍

如果子树size超过sqrt了,由于整棵树不超过n,所以个数不超过sqrt个

将这sqrt个物品,直接用bitset优化背包转移

这部分复杂度O(n*sqrt(n)/w),w为bitset位长,一般取32或64

补充说明

如果对小于sqrt的每个物品暴力做二进制拆位优化多重背包,

会导致复杂度会多一个log(sqrt(n)),但也比较小,本题允许通过

代码

#include 
using namespace std;
const int N=1000013;
typedef long long ll;
vectormp[N];
int p[N],sz[N];
ll ans=0;
vectorsizes;
int tot=0;
int c[1010];
templatevoid solve(){
    if(LEN<=tot){solve();return;}
    bitsetB;B[0]=1;
    const int SB=(int)sqrtl(tot);
    vectorsize2;
    for(auto s:sizes)
        if(s=SB){
    		for(int j=1;j<=w;++j){
    			size2.push_back(i*2);
    		}
    	}
    	else{
    		c[i*2]+=w;
    	}
    	c[i]=1+(c[i]-1)%2;
 	    for(int j=1;j<=c[i];++j)size2.push_back(i);
 	    c[i]=0;
    }
    for(auto s:size2)B|=B<=tot/2){
        ans+=(ll)s*(tot-s);sizes.clear();
        return;
    }
    solve<1>(), sizes.clear();
}

void solve(){
    int n=1000000;
    cin>>n;
    for(int i=2;i<=n;i++){
        cin>>p[i];
        mp[p[i]].push_back(i);
    }
    dfs(1,0);
    cout<>T;
    while(T--)solve();
}

你可能感兴趣的:(#,dp优化,背包,bitset优化,01背包,多重背包)