标签:sig 选择 fir force 问题 ref lan void else
给你一个长度为\(n\)的\(01\)串\(s\)。你可以进行不超过\(n-1\)次操作。每次操作,你可以选择一个位置\(i\) (\(1\leq i<|s|\)),令\(s_i:=\max(s_i,s_{i+1})\),然后删掉\(s_{i+1}\)。删掉后,左右两边会自动拼接起来,并且\(s\)的长度会减\(1\)。请求出,有多少种不同的串,能通过对\(s\)进行不超过\(n-1\)次操作得到。答案对\(10^9+7\)取模。
数据范围:\(1\leq n\leq 10^6\)。
这个操作,相当于每个\(1\),可以“吃掉”它旁边的任何一个东西。当然,两个\(0\)在一起时,一个\(0\)也可以“吃掉”另一个,不过这不重要:原串全是\(0\)时我们可以特判,其他情况下只需要用到“拿\(1\)吃别人”的操作。
假设给你一个结果串\(t\)。如何判断\(t\)能不能由\(s\)经过操作得来呢?贪心地,依次考虑\(t\)的每一位。假设现在考虑到了\(t\)的前\(j\)位,已经用掉了\(s\)的前\(i\)位(显然\(i\)越小,“匹配”成功的机会越大,所以我们的贪心就是在每一步让\(i\)尽可能小)。
以上是已经确定\(t\)的时候,可以做这样一个贪心。现在我们要求出有多少满足条件的\(t\),那么可以对这个贪心进行DP。
设\(dp[i]\)表示有多少串\(t\)能贪心匹配到\(s\)的第\(i\)位。转移时,分别考虑\(t\)的下一位是\(0\)或\(1\)的情况,转移到两种\(i‘\)。如果先预处理出,每个\(0\)和它前一个\(1\)的距离,然后暴力向后找\(i‘\),复杂度是\(O(n)\)的。但我们可以先从\(n\)到\(1\)倒着推一遍,就能预处理出每个\(i\)对应的\(i‘\)了,转移的复杂度变成\(O(1)\)。
求答案时,枚举一个\(i\)作为结尾,把这样的\(dp[i]\)加起来就是答案了。不过,并不是所有\(i\)都能作为结尾。我们发现,一个\(i\)能作为结尾,当且仅当它与上一个\(1\)的距离小于等于\(s_n\)与上一个\(1\)的距离(如果某个位置本身就是\(1\),可以认为距离为\(0\)),否则,它后面的东西消不掉,这个\(i\)自然就不能作为结尾了。
还有一个值得思考的问题。我们的DP是每次在已经有的串后面添加一个字符,得到新的串,那会不会重复呢?比如,已有\(101\)和\(10\)两个串,如果此时在后面添加一个\(1\),得到两个新串\(1011\)和\(101\),而\(101\)就与前面的串重复了。其实这种情况不会出现,因为每个结果串\(t\),在这种贪心下,会唯一对应一个匹配到结尾的\(i\)。所以算答案时,每个\(t\)只在唯一一个\(dp[i]\)里被统计到。那么“同一个\(dp[i]\)”里会不会有重复呢?也不会。因为把\(t\)的最后一位去掉后,还是一个串,还是匹配到唯一的\(i‘\),所以\(t\)只会被从\(i‘\)到\(i\)转移一次,如果\(dp[i‘]\)里没有重复,\(dp[i]\)里也不会有重复,这样归纳下去即可。
时间复杂度\(O(n)\)。
参考代码:
//problem:CF1383E
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
const int MOD=1e9+7;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int& x,int y){x=mod1(x+y);}
inline void sub(int& x,int y){x=mod2(x-y);}
const int MAXN=1e6;
int n,dp[MAXN+5],dist[MAXN+5],pos[MAXN+5],nxt_1[MAXN+5],nxt_d[MAXN+5];
char s[MAXN+5];
int main() {
cin>>(s+1); n=strlen(s+1);
int start_pos=0;
for(int i=1;i<=n;++i){
if(s[i]==‘1‘){
start_pos=i;
break;
}
}
if(!start_pos){
//全是0
cout<<n<<endl;
return 0;
}
for(int i=1;i<=n;++i){
//dist[i]: i到上一个1的距离
if(s[i]==‘0‘){
dist[i]=dist[i-1]+1;
}
else{
dist[i]=0;
}
}
for(int i=0;i<=n+1;++i)
pos[i]=n+1;
for(int i=n;i>=1;--i){
nxt_1[i]=pos[0];
nxt_d[i]=pos[dist[i]+1];
pos[dist[i]]=i;
}
dp[start_pos]=start_pos;
for(int i=start_pos;i<n;++i){
if(nxt_1[i]!=n+1){
add(dp[nxt_1[i]],dp[i]);
}
if(nxt_d[i]!=n+1){
add(dp[nxt_d[i]],dp[i]);
}
}
int ans=0;
for(int i=start_pos;i<=n;++i){
if(dist[i]<=dist[n]){
add(ans,dp[i]);
}
}
cout<<ans<<endl;
return 0;
}
标签:sig 选择 fir force 问题 ref lan void else
原文地址:https://www.cnblogs.com/dysyn1314/p/13376474.html