CF1383E Strange Operation
一、题目
二、解法
草你吗老子以前写得什么几把,真他吗狗屁不通,我一怒之下重构博客。
直接建 \(\tt DFA\) 来算就行了,节点就是每个位置,转移我们考虑往每个位置后面加入 \(0/1\)
如果加入 \(1\),我们直接找到下一个 \(1\) 即可。
如果加入 \(0\),如果下一个位置是 \(0\) 或者这个位置是 \(1\),那么直接找到后面第一个 \(0\),否则这个位置是一段连续 \(0\) 的末尾,考虑继续加入 \(0\) 等效为选取后面更长的一段,从那一段等长的部分开始加 \(0\),可以用单调栈来建立转移。
考虑起始节点是第一个 \(1\),可以在任意 \(1\) 处终止(这要求我们除去开头和结尾的 \(0\)),那么直接在 \(\tt DFA\) 上面计数即可,时间复杂度 \(O(n)\)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 1000005;
const int MOD = 1e9+7;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,l,r,ans,nm[M],p[M],g[M][2],f[M];char s[M];
void add(int &x,int y) {x=(x+y)%MOD;}
signed main()
{
scanf("%s",s+1),n=strlen(s+1);
l=1;while(s[l]=='0') l++;
r=n;while(s[r]=='0') r--;
if(l>r) {printf("%d\n",n);return 0;}
for(int i=r,ls=0;i>=l;i--)
{
g[i][1]=ls;
if(s[i]=='1') ls=i;
}
for(int i=r,ls=0;i>=l;i--)
{
if(s[i+1]=='0' || s[i]=='1') g[i][0]=ls;
if(s[i]=='0') ls=i;
}
for(int i=l,nw=0;i<=r;i++)
{
if(s[i]=='0')
{
nw++;
while(m && nm[m]<nw)
g[p[m]][0]=i,m--;
}
else
p[++m]=i-1,nm[m]=nw,nw=0;
}
f[l]=1;
for(int i=l;i<=r;i++)
{
if(s[i]=='1') add(ans,f[i]);
for(int j=0;j<2;j++) if(g[i][j])
add(f[g[i][j]],f[i]);
}
printf("%d\n",1ll*ans*l%MOD*(n-r+1)%MOD);
}