CF1383E Strange Operation

小清新 Counting 题,想到转化成序列计数后就不难了

考虑将一个 0/1 串等价转化为一个刻画相邻两个 \(1\) 之间有几个 \(0\) 的序列

比如样例中的 \(00101100011100\) 就可以转化为 \(\{2,1,0,3,0,0,2\}\) 这个序列,显然转化后的序列和原来的 0/1 串等价

考虑此时一次操作相当于将序列中某个数减 \(1\);或者将某个不在末尾的 \(0\) 删去

因此可以用类似自动机的思路判定一个序列是否可以由原始的序列 \(\{a\}\) 生成

即令 \(f_i\) 表示匹配到原序列的第 \(i\) 个位置,对应的序列的总方案数,转移考虑枚举在序列末尾加上的数 \(x\),则可以转移到 \(i\) 之后的第一个位置 \(j\),满足 \(a_j\ge x\)

不妨换一个思路,考虑 \(f_i\)\(f_j\) 的贡献,手玩一下会发现系数就是 \(a_j-\max_\limits{i<k<j} a_k\)

这种涉及到 \(\max\) 的问题很容易想到单调栈,我们从前往后维护一个递减的单调栈,每次弹栈的时候更新下贡献即可

代码是祁神写的,复杂度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int INF = (int)1e18+5;
const int MOD = (int)1e9+7;
const int N = (int)1e6+5;

void add(int &x, int a){if ((x+=a)>=MOD) x-=MOD;}

string str;
int dp[N], sum[N];
struct Node{
    int pos, bd;
};
Node stk[N]; int top=-1;

signed main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> str;
    int len = str.length();
    vector<int> space;
    int cnt0=0;
    for (int i=0; i<len; ++i){
        if ('0'==str[i]) ++cnt0;
        else space.push_back(cnt0), cnt0=0;
    }

    // printf("space:"); for (int i=0; i<space.size(); ++i) printf("%lld ", space[i]); printf("cnt0=%lld\n", cnt0);

    if (0==space.size()){
        cout << cnt0 << '\n';
        return 0;
    }else if (1==space.size()){
        cout << (cnt0+1)*(space[0]+1)%MOD << '\n';
        return 0;
    }
    int bis = (cnt0+1)*(space[0]+1)%MOD;
    int sz = space.size();
    space[0] = INF;
    stk[++top] = Node{0, -1};
    dp[0] = 1;
    for (int i=1; i<sz; ++i){
        while (top>=0 && space[stk[top].pos] <= space[i]){
            auto [pos, bd] = stk[top--];
            add(dp[i], dp[pos]*(space[i]-bd)%MOD);
            add(dp[i], (MOD+sum[pos-1]-sum[stk[top].pos])%MOD*(space[i]-space[pos])%MOD); 
        }
        add(dp[i], dp[stk[top].pos]*(space[i]-stk[top].bd)%MOD);
        sum[i] = (sum[i-1]+dp[i])%MOD;
        stk[top].bd = space[i];
        stk[++top] = Node{i, -1};
    }

    // printf("dp:"); for (int i=0; i<sz; ++i) printf("%lld ", dp[i]); puts("");
    

    int ans=0;
    for (int i=0; i<sz; ++i) add(ans, dp[i]);
    ans = ans*bis%MOD;
    cout << ans << '\n';
    return 0;
}
posted @ 2024-08-14 19:15  空気力学の詩  阅读(18)  评论(0编辑  收藏  举报