OI - Strange Operation(Codeforces) | Lucky_Glass's Blog
0%

OI - Strange Operation(Codeforces)

DP题好多ya :(


# 题面

> Linked Codeforces 1383E

点击展开/折叠题面

给定一个01串 $S$($|S|\le10^6$),你可以进行任意次操作,每次选择相邻的两个位置 $i,i+1$,用 $\max\{S_i,S_{i+1}\}$ 替换 $S_i,S_{i+1}$。
求能够得到多少种不同的01串。


# 解析

如果 $S$ 中原本就有 $1$,那么任何一个可以得到的 01 串都可以看作是 $1$“吞并”了旁边的 $0$ 得到的。(如果没有 $1$ 就特判一下)

设 $S$ 的最左端有连续的 $a$ 个 $0$,那么我们可以先将这些 $0$ 删去后求得答案 $x_0$。此时求得的 01 串的左边一定是 $1$,所以可以将所得的每个 01 串左端加上 $0\sim a$ 个 $0$ 得到不同的 01 串。于是最终答案就是 $ax_0$。

考虑当前的 $S$ 左端是 $1$,求 $x_0$。于是可以设计DP,对于原串中的每个 $1$,决策它向右合并后在右边剩下了多少连续的 $0$。定义状态 $f_i$ 表示原串的第 $i$ 个 $1$ 为开头的后缀能够得到多少种 01 串。假如现在要计算原串的第 $i$ 个 $1$ 向右合并后,右边有 $j$ 个连续的 $0$,我们需要找到原串中在 $i$ 之后的第一次出现的连续的 $j$ 个 $0$,从下一个 $1$ 的位置 $k$ 转移过来。其中“第一次出现”保证了能够构造出尽可能多的 01 串。

但是还有一种特殊情况,设 $S$ 的右端有 $b$ 个连续的 $0$:将第 $i$ 个 $1$ 之后的 $1$ 全部合并,然后再在末尾留下 $0\sim b$ 个 $0$。

于是可以得到DP转移式:

其中的 $k$ 是找到的第一个前面有至少 $j$ 个连续的 $0$ 的 $1$ 的标号。

我们发现,如果 $p,q$ 满足 $p<q$ 且 $p$ 前面连续的 $0$ 的个数大于等于 $q$ 前面连续的 $0$ 的个数,那么 $q$ 永远不会成为转移点 $k$。于是可以用单调栈维护。


# 源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*Lucky_Glass*/
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1e6+10,MOD=1e9+7;
#define ci const int &
#define val(i) (pos[i]-pos[(i)-1])

inline int Add(ci a,ci b){return a+b>=MOD? a+b-MOD:a+b;}
inline int Sub(ci a,ci b){return a-b<0? a-b+MOD:a-b;}
inline int Mul(ci a,ci b){return int(1ll*a*b%MOD);}

char str[N];
int pos[N],f[N];
int n,m;
stack<int> stk;

int main(){
scanf("%s",str+1);
n=(int)strlen(str+1);
for(int i=1;i<=n;i++)
if(str[i]=='1')
pos[++m]=i;
if(!m){printf("%d\n",n);return 0;}
stk.push(m);
int sufextra=n-pos[m]+1,preextra=pos[1];
f[m]=sufextra;
int tot=Mul(val(m),f[m]);
for(int i=m-1;i>=1;i--){
f[i]=Add(tot,sufextra);
int las=0,vali=val(i);
while(!stk.empty() && vali>=val(stk.top())){
int j=stk.top(),valj=val(j);
tot=Sub(tot,Mul(Sub(valj,las),f[j]));
las=valj;
stk.pop();
}
if(!stk.empty()){
int j=stk.top(),valj=val(j);
tot=Sub(tot,Mul(Sub(valj,las),f[j]));
tot=Add(tot,Mul(Sub(valj,vali),f[j]));
}
tot=Add(tot,Mul(vali,f[i]));
stk.push(i);
}
printf("%d\n",Mul(f[1],preextra));
return 0;
}

THE END

Thanks for reading!

> Linked 象牙塔少女 αrtist5-网易云