Manci的序列题解

Menci 的序列

题意:你有一个长为 n 的序列,每个位置是 * 或者 +* 表示让变量 ×2+ 表示让变量 +1。现在你要选出它的一个子序列,使得一个初始为 0 的变量在对子序列中的字符依次执行对应操作后对 2k 取模所得结果尽可能大。求出最大可能的结果。

sol:比较显然的一点,最后的值为2cici 表示一个加号后面乘号的个数。于是可以先考虑一种非常简单的特殊情况:任意两个乘号之间最多只有一个加号。可以从前往后直接贪心做,最开始填第 k1 位,如果乘号不够就填第 k2 位,如果足够就填 1 再考虑之后的加号,以此类推可以得到最优解。但是现在原问题两乘号之间会有多个加号,可以用二进制分组的思路,因为一个加号后有 x 个乘号相当于两个加号后有 x1 个加号。

这里略微比较复杂,举一个例子原序列为+++ 可替换为 +*+ ,具体的,原序列选 + 等价于新序列选 + ,原序列选 ++ 等价于新序列选 +* 原序列选 +++ 等价于新序列选 +*+ 。所以我们可以把后面有 i 个乘号的加号每两个换成一个后面有 i+1 的加号,但是不能全部转完,需要留下 12 个,否则原序列某些选择在新序列中找不到对应的选法。

就这样我们把原序列转化成了连续加号个数不超过 2 个的特殊序列。只有一个的情况上面已经讲了,而两个也差不多,具体的,处理时如果前一位需要填 1 但没有加号了可以临时进位,否则就只需要在这一位填 1 即可。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
inline int rd()
{
	char c;int f=1;
	while(!isdigit(c=getchar()))if(c=='-')f=-1;
	int x=c^48;
	while(isdigit(c=getchar()))x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
int a[N],ans[N],t,cnt,k,n,m,p;
char s[N];
int main()
{
	n=rd(),k=rd();
    scanf("%s",s+1);
    for(int i=n;i;i--)
        if(s[i]=='*') a[++cnt]=0;
		else a[cnt]++;
    for(int i=0;i<=n;i++)
	{
		if(!a[i]) continue;
        t=(a[i]-1)/2;
        a[i]-=2*t,a[i+1]+=t;
    }
    p=k-1;
    for(int i=n;i;i--)
	{
        while(i>=0&&!a[i]) i--;
        if(i<0) break;
        if(i>=p) ans[p--]=1;
        else
		{
            for(int j=0;j<p;j++){
                t=a[j]/2;
                a[j]-=2*t,a[j+1]+=t;
                ans[j]=a[j];
            }
            ans[p]=a[p];
            break;
        }
    }
    p=k-1;
    while(p>=0&&!ans[p]) p--;
    if(p>=0) for(int i=p;i>=0;i--) printf("%d",ans[i]);
    else printf("0");
    return 0;
}

posted @   Re_Star  阅读(24)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示