1481F.AB Tree(树上信息统计+01背包+记录DP路径+Bitset优化时间复杂度)

 题意:

给出一棵树,根节点为1。你可以给x个点赋值A,剩下n-x个点赋值B。询问从根节点到每个点的路径构成的字符串的最小集合。

题解:

结论1:每一层的点赋相同的值,可以使答案最优。

所以可以把每一层看成物品,物品的价值就是这一层的点数,跑一个01背包。

如果存在一组物品使得它们的和恰好为x,那么就直接输出层数即可。

如果不存在,最多也只会是层数+1。

结论2:对单层修改叶子节点的值,对答案的影响最小。

先找到叶子节点最多的那一层,试图通过改这一层的叶子节点的状态来弥补DP状态。

结果T15。

这是TLE的代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int n,x;
vector<int> g[maxn];
vector<int> d[maxn];
int cnt[maxn];
int m;
void dfs (int x,int f,int dep) {
    m=max(m,dep);
    d[dep].push_back(x);
    if (g[x].size()==1) cnt[dep]++;
    for (int y:g[x]) {
        if (y==f) continue;
        dfs(y,x,dep+1);
    }
}
map<int,int> f[maxn];
int ans[maxn];
int ff=0;
int main () {
    scanf("%d%d",&n,&x);
    if (x>n-x) x=n-x,ff=1; 
    for (int i=2;i<=n;i++) {
        int y;
        scanf("%d",&y);
        g[i].push_back(y);
        g[y].push_back(i);
    }
    dfs(1,0,1);
    //for (int i=1;i<=m;i++) printf("%d ",d[i].size());
    //rintf("\n");
    f[0][0]=1;
    for (int i=1;i<=m;i++) {
        for (int j=x;j>=0;j--) f[i][j]|=f[i-1][j];
        for (int j=x;j>=d[i].size();j--) {
            f[i][j]|=f[i-1][j-d[i].size()];
        }
    }
    if (f[m][x]) {
        printf("%d\n",m);
        for (int i=m;i>=1;i--) {
            if (d[i].size()<=x&&f[i-1][x-d[i].size()]) {
                x-=d[i].size();
                for (int v:d[i]) ans[v]=1;
            }
        }
        if (!ff)
            for (int i=1;i<=n;i++) if (ans[i]==1) printf("a");else printf("b");
        else
            for (int i=1;i<=n;i++) if (ans[i]==1) printf("b");else printf("a");
    }
    else {
        printf("%d\n",m+1);
        for (int i=0;i<=m;i++) f[i].clear();
        f[0][0]=1;
        int Max=0,u=-1;
        for (int i=1;i<=m;i++) if (cnt[i]>Max) {
            Max=cnt[i];
            u=i;
        }
        for (int i=1;i<=m;i++) {
            for (int j=x;j>=0;j--) f[i][j]|=f[i-1][j];
            if (i==u) continue;
            for (int j=x;j>=d[i].size();j--) {
                f[i][j]|=f[i-1][j-d[i].size()];
            }
        } 
        int ed=-1;
        for (int i=0;i<x;i++) {
            if (f[m][i]&&i+Max>=x) {
                ed=i;
                break;
            }
        }
        int tt=x-ed;
        for (int v:d[u]) {
            if (g[v].size()>1) continue;
            if (!tt) break;
            tt--;
            ans[v]=1;
        }
        for (int i=m;i>=1;i--) {
            if (i==u) continue;
            if (d[i].size()<=ed&&f[i-1][ed-d[i].size()]) {
                ed-=d[i].size();
                for (int v:d[i]) ans[v]=1;
            }
        }
        if (!ff)
            for (int i=1;i<=n;i++) if (ans[i]==1) printf("a");else printf("b");
        else
            for (int i=1;i<=n;i++) if (ans[i]==1) printf("b");else printf("a");
    }
}
View Code

看了别人的题解才明白,这里需要套一个Bitset优化背包求解的过程。对比原来的代码理解一下吧。。。

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
const int inf=1e9;
int n,m,x;
int dep[maxn];//节点在第几层 
int num[maxn];//每一层的节点个数
int lev[maxn];//每一层的叶子节点个数
bitset<maxn> f[2500];
int w[maxn];
int ok[maxn];
vector<int> g[maxn],v[maxn];
unordered_map<int,int> vis;
void dfs1 (int x,int pre) {
    dep[x]=dep[pre]+1;
    num[dep[x]]++;
    m=max(m,dep[x]);
    if (g[x].size()==1) lev[dep[x]]++;
    for (int y:g[x]) {
        if (y==pre) continue;
        dfs1(y,x);
    }
}
void dfs2 (int x,int y) {
    //找dp状态
    if (x==0) return;
    for (int i=0;i<v[x].size();i++) {
        if (w[x]>y||f[x-1][y]) break;
        y-=w[x];
        ok[v[x][i]]=1;
    } 
    dfs2(x-1,y);
}
int main () {
    scanf("%d%d",&n,&x);
    for (int i=2;i<=n;i++) {
        int y;
        scanf("%d",&y);
        g[i].push_back(y);
        g[y].push_back(i);
    }
    dfs1(1,0);
    int cnt=0;
    for (int i=1;i<=m;i++) {
        //第一步,把相同点数的层合并
   
        if (vis[num[i]]) {
            v[vis[num[i]]].push_back(i); 
        }
        else {
            vis[num[i]]=++cnt;
            w[cnt]=num[i];
            v[cnt].push_back(i);
        }
    }
    f[0][0]=1; 
    for (int i=1;i<=cnt;i++) {
        f[i]=f[i-1]; 
        int sz=v[i].size();
        for (int j=1;j<=sz;j<<=1) {
            sz-=j;
            f[i]|=(f[i]<<(j*w[i]));
        }
        if (sz) f[i]|=f[i]<<(sz*w[i]);//这一步还没懂 
    }
    if (f[cnt][x]) {
        printf("%d\n",m);
        dfs2(cnt,x);
        for (int i=1;i<=n;i++) {
            if (ok[dep[i]])
                printf("a");
            else    
                printf("b");
        }
    }
    else {
        int ans=inf;
        for (int i=x;i>=0;i--) if (f[cnt][i]) {
            ans=i;
            break;
        }
        dfs2(cnt,ans);
        int pp=-1;
        for (int i=1;i<=m;i++) {
            if (!ok[i]&&lev[i]>=x-ans) {
                pp=i;
                break;
            }
        }
        printf("%d\n",m+1);
        for (int i=1;i<=n;i++) {
            if (dep[i]==pp&&g[i].size()==1) {
                if (ans==x)
                    printf("b");
                else
                    printf("a"),ans++;
            }
            else {
                if (ok[dep[i]])
                    printf("a");
                else
                    printf("b");
            }
        }
    }
}
 

 

posted @ 2021-02-10 15:56  zlc0405  阅读(130)  评论(0编辑  收藏  举报