专栏文章
CF1481F
CF1481F题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miqbgvx0
- 此快照首次捕获于
- 2025/12/04 02:04 3 个月前
- 此快照最后确认于
- 2025/12/04 02:04 3 个月前
本题解仅为一种线性空间做法的介绍。其他部分不再赘述。
其实就是一个小技巧:线性空间记录 bool 背包每个大小的转移是放入了什么物品。关键在于,注意到对于每个背包大小 ,我们只需要在 第一次为 时记录这次转移中放入了什么就行。
这样构造的正确性也相当显然。设上述大小为 的背包,放入的物品为 。假设 是从 转移过来,那么在更新 , 一定有值,并且以后不再修改。同时 显然比 更晚放入背包,所以构造时依次取出的物品不会重复,方案合法。
其实主要还是自己想想才好理解。
然后这个东西也厉害在它可能兼容 bitset 优化 bool 背包。具体地,你只需要将 和 的 bitset 异或一下就能得到在 第一次为 的 ,然后记录 。由于这样的 总共不超过 个,所以配合 _Find_next 相关操作,这一部分总复杂度为 ,大部分情况下可以接受。
code:
CPPint n,m,dep[N];
pii frm[N];
bool dp[N],ans[N];
vector<int> f[N],g[N],h[N];
int tot,head[N];
struct node{
int to,nxt;
}e[N];
il void add(int u,int v){
e[++tot]={v,head[u]},head[u]=tot;
}
void dfs(int u,int f){
dep[u]=dep[f]+1;
if(head[u]){
g[dep[u]].eb(u);
}else{
h[dep[u]].eb(u);
}
go(i,u){
int v=e[i].to;
dfs(v,u);
}
}
void Yorushika(){
read(n,m);
rep(i,2,n){
int u;read(u);
add(u,i);
}
dfs(1,0);
rep(i,1,n){
f[g[i].size()+h[i].size()].eb(i);
}
dp[0]=1;
rep(i,1,n){
if(f[i].size()){
int t=f[i].size();
for(int j=1;j<=t;t-=j,j<<=1){
drep(k,n,j*i){
dp[k]|=dp[k-j*i];
}
rep(k,1,n){
if(!frm[k].fi&&dp[k]){
frm[k]=Mp(i,j);
}
}
}
if(t){
drep(k,n,t*i){
dp[k]|=dp[k-t*i];
}
rep(k,1,n){
if(!frm[k].fi&&dp[k]){
frm[k]=Mp(i,t);
}
}
}
}
}
int mx=0;
rep(i,1,n){
mx=max(mx,dep[i]);
}
if(dp[m]){
printf("%d\n",mx);
int u=m;
while(u){
auto [x,y]=frm[u];
rep(_,1,y){
if(!f[x].size()){
break;
}
ans[f[x].back()]=1;
f[x].pop_back();
}
u-=x*y;
}
rep(i,1,n){
pc('a'+!ans[dep[i]]);
}
}else{
printf("%d\n",mx+1);
bool fl=0;
int x=m,y=n-x;
rep(i,1,n){
if(g[i].size()>x){
swap(x,y),fl^=1;
}
for(int j:g[i]){
ans[j]=fl;
x--;
}
for(int j:h[i]){
if(!x){
swap(x,y),fl^=1;
}
ans[j]=fl;
x--;
}
}
rep(i,1,n){
pc('a'+ans[i]);
}
}
}
signed main(){
int t=1;
//read(t);
while(t--){
Yorushika();
}
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...