原题: 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);
}