CF1383E Strange Operation
一、题目
点此看题
二、解法
大体思路自己想的(独立思考乐趣多??),最后的建图参考了SharpnessV大佬的方法。
类似子序列自动机的方法,我们给每个点找一个后继节点。但是本题的要求显然更高,它要求我们知道子序列自动机的原理:找到最有潜力的后继节点就可以表达所有方案。
所谓“潜力”,也就是以它开始的选择范围更广,所以常规算法我们直接找后面第一个点即可。
但这样做的前提是把局部本质相同的方案用这个后继节点来表达,但由于本题的特殊性,我们考虑区分局部方案的依据就是相邻两个 \(1\) 之间 \(0\) 的个数(你可以理解成把 \(1\) 作为分界点来动态规划),所以对于每一种 \(0\) 的个数我们需要找到一个最有潜力的后继,但由于本题连续的 \(0\) 可以继承,所以我们是把一个代表 \(0\) 个数的区间连到原序列一个区间的第一个点上面去。
如果你完全理解我上面所说,就可以认同下面巧妙的建图方法了:
- 对于原序列中的 \(0\),如果添上 \(0\),若是某一段连续 \(0\) 的末尾,需要找到后面第一个长度大于当前段的连续段,连到连续段的第一个点即可,否则直接连到下一个 \(0\);如果添上 \(1\),直接连后面第一个 \(1\) 即可。
- 对于原序列中的 \(1\),直接连向后面第一个 \(0\)、后面第一个 \(1\) 即可。
所以用单调栈就可以很轻易地把图建出来,时间复杂度 \(O(n)\)
三、总结
字符串本质不同计数问题中,匹配思想很重要??
#include
#include
#include
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]