[loj#560]Menci的序列

题目大意

一个人长度为n的字符串s,只包含+和*。
选出一个子序列,然后你有一个ret,初始为0,按顺序扫你选出的这个子序列。
如果碰到的是+,ret+1,否则ret*2。
最大化ret%2^k。

做法

lca题解写的真麻烦。
首先我们可以看成,每个+贡献2^c,c为该+后面的*数量。
然后就能注意到:
* + + +
+ * +
这两个是等价的。
因此假如出现连续三个+,我们把其中两个+删掉往前一个*的前面丢一个。
注意开头任意个*都没所谓,因此一开始在开头添加充分多个。
这样把s变成了s’,满足性质+段不会超过2个。
记a[i]表示有多少个+号后面有i个*。
如果a[0]~a[k-1]都不是0,显然答案是2^k-1。
否则一定存在某位为0,设j为最大的j其中a[j]为0。
大于j的每个都能选1。
发现由于a[i]<=2,小于j的+号全选都不会在该位产生进位,因此肯定全选。

#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1000000+10;
char s[maxn];
int a[maxn],ans[maxn];
int i,j,k,l,t,n,m,p;
int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    t=0;
    fd(i,n,1)
        if (s[i]=='*') t++;else a[t]++;
    n+=10;
    fo(i,0,n){
        t=(a[i]-1)/2;
        a[i]-=2*t;
        a[i+1]+=t;
    }
    p=k-1;
    while (n>=0){
        while (n>=0&&!a[n]) n--;
        if (n<0) break;
        if (n>=p) ans[p--]=1;
        else{
            fo(i,0,p-1){
                t=a[i]/2;
                a[i]-=2*t;
                a[i+1]+=t;
            }
            fo(i,0,p) ans[i]=a[i];
            break;
        }
        n--;
    }
    p=k-1;
    while (p>=0&&!ans[p]) p--;
    if (p>=0){
        fd(i,p,0) printf("%d",ans[i]);
        printf("\n");
    }
    else printf("0\n");
}

你可能感兴趣的:(贪心)