The Fair Nut and Strings(想法 搜索)

原题: https://cn.vjudge.net/problem/CodeForces-1084E

题意:

所有串的长度为n,只含有ab字母,现在给两个串S和T,你可以任意选择k个串,其字典序排在S和T的中间,使这k个串的所有前缀串的集合的元素个数最大。

解析:

一个000串贡献的个数为3(0、00、000)
再来一个001,只会多一个001,但是再来一个100会多出三个。所以,你选择的这k个串,公共前缀越短越好。我们将所有可能画成一棵树的形式
在这里插入图片描述为了使多个串的前缀不同的长度更长,我第一次选择红色,第二次选择黄色,第三次选择蓝色。

把这棵树压扁,计算每个位置的节点个数,这棵树变成了数组{2,3,5,7},观察可得:可以选择2次长度为4的不同的串,接下来第一个位置不可能不同了,选择1次长度为3的,再2次2的,2次1的。

得出来的就是答案,当然,中途k用完了就break了。

但是有一个问题,如果遍历到一个位置没有考虑了,即后面是a是b都是在S和T的中间的话,接下来就是完全满二叉树了,难到一遇到这种情况我要直接遍历更新吗?显然不行,所以记一个标记,代表这个位置有一个接下来为满二叉树的分支

#include<bits/stdc++.h> 
using namespace std; 
#define LL long long 
#define debug(i) printf("# %d\n",i) 
char u[500009],d[500009];
LL f[500009],ct[500009];
LL n,k;
void dfs(int p,bool up,bool down){
    if(p>n)return;
    if(!up&&!down)
        if(u[p]==d[p])ct[p]=1,dfs(p+1,0,0);
        else ct[p]=2,dfs(p+1,0,1),dfs(p+1,1,0);
    else if(up&&!down)
        if(d[p]=='a')f[p]++,ct[p]++,dfs(p+1,up,down);
        else ct[p]++,dfs(p+1,up,down);
    else if(!up&&down)
        if(u[p]=='b')f[p]++,ct[p]++,dfs(p+1,up,down);
        else ct[p]++,dfs(p+1,up,down);
    else ;
}

int main(){
    scanf("%lld%lld",&n,&k);
    scanf("%s%s",d+1,u+1);
    dfs(1,0,0);    
    LL now=0;
    LL use=0;
    LL ans=0;
    for(int i=1;i<=n;i++){
        now<<=1;now+=f[i];
        LL cnt=now-use+ct[i];
        
        ans+=min(cnt,k)*(n-i+1);
        k-=cnt;
        if(k<=0)break;
        use+=cnt;
    }
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/85140897