【ZROI】【启发式合并】【17 提高 2】Last mile of the way

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/FYOIER/article/details/80955345

做法就是每次将一个点的所有子节点的答案合并到这个点上去,暴力的合并可以每次枚举子节点背包的大小,这样的复杂度是O(ns2)的。可以利用启发式合并的trick,每次将子节点中最重的儿子直接复制过来,然后对于其他子树中的所有节点直接暴力合并,由于合并单个节点的复杂度是O(s)的,而每次合并了以后子树大小至少变成了原来的2倍,所以每个节点最多被合并log2n次,这样,复杂度就是O(nslogn)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 5006
#define all 5000
#define LL long long
using namespace std;
int n,T,tot[2],num[maxn],w[maxn],p[maxn],lnk[2][maxn],son[2][maxn*2],nxt[2][maxn*2];
LL f[maxn][maxn];
bool vis[maxn];
void add(int t,int x,int y){
    nxt[t][++tot[t]]=lnk[t][x];son[t][tot[t]]=y;lnk[t][x]=tot[t];
}
void dfs(int x){
    vis[x]=0;num[x]=1;
    for(int j=lnk[0][x];j;j=nxt[0][j]) if(vis[son[0][j]]){
        add(1,x,son[0][j]);dfs(son[0][j]);num[x]+=num[son[0][j]];
    }
}
void merge(int t,int x){
    for(int j=all;j>=w[x];j--)f[t][j]=max(f[t][j],f[t][j-w[x]]+p[x]);
    for(int j=lnk[1][x];j;j=nxt[1][j])merge(t,son[1][j]);
}
void solve(int x){
    int Max=0,t=0;vis[x]=0;
    for(int j=lnk[1][x];j;j=nxt[1][j]){
        if(num[son[1][j]]>Max)Max=num[son[1][j]],t=son[1][j];
        solve(son[1][j]);
    }
    if(t)memcpy(f[x],f[t],sizeof(f[x]));
    for(int j=all;j>=w[x];j--)f[x][j]=max(f[x][j],f[x][j-w[x]]+p[x]);
    for(int j=lnk[1][x];j;j=nxt[1][j]) if(son[1][j]!=t)merge(x,son[1][j]);
}
int main(){
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    scanf("%d",&n);
    for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(0,x,y),add(0,y,x);
    for(int i=1;i<=n;i++)scanf("%d%d",&p[i],&w[i]);
    memset(vis,1,sizeof(vis));
    dfs(1);solve(1);
    scanf("%d",&T);
    while(T--){
        int x,s;
        scanf("%d%d",&x,&s);
        printf("%lld\n",f[x][s]);
    }
    return 0;
}
展开阅读全文

没有更多推荐了,返回首页