题解 CF1383E Strange Operation
Posted dysyn1314
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 CF1383E Strange Operation相关的知识,希望对你有一定的参考价值。
题目大意
给你一个长度为(n)的(01)串(s)。你可以进行不超过(n-1)次操作。每次操作,你可以选择一个位置(i) ((1leq i<|s|)),令(s_i:=max(s_i,s_{i+1})),然后删掉(s_{i+1})。删掉后,左右两边会自动拼接起来,并且(s)的长度会减(1)。请求出,有多少种不同的串,能通过对(s)进行不超过(n-1)次操作得到。答案对(10^9+7)取模。
数据范围:(1leq nleq 10^6)。
本题题解
这个操作,相当于每个(1),可以“吃掉”它旁边的任何一个东西。当然,两个(0)在一起时,一个(0)也可以“吃掉”另一个,不过这不重要:原串全是(0)时我们可以特判,其他情况下只需要用到“拿(1)吃别人”的操作。
假设给你一个结果串(t)。如何判断(t)能不能由(s)经过操作得来呢?贪心地,依次考虑(t)的每一位。假设现在考虑到了(t)的前(j)位,已经用掉了(s)的前(i)位(显然(i)越小,“匹配”成功的机会越大,所以我们的贪心就是在每一步让(i)尽可能小)。
- 如果(t)的第(j+1)位是(1)。我们找出(s)里(i)后面的第一个(1),也就最小的(i‘)满足(i‘>i)且(s_{i‘}=1),然后用(i‘)吃光([i+1,i‘-1])里所有的数。再令(i:=i‘)。
- 如果(t)的第(j+1)位是(0)。假设从(t)里的上一个(1)开始(如果没有就从开头开始),直到(j+1),这段区间里共有(k)个(0)。在(s)里找到下一个有(k)个(0)的段:也就是最小的(i‘)满足(i‘>i)且(forall0leq k‘<k:s_{i‘-k‘}=0)。如果(i‘=i+1)(也就是(i)所在的这段(0)本身就够长),直接把这个(0)接在后面即可,如果(i‘>i+1)(说明跳到了后面的某一段),那用(i‘-k)(一定是个(1))把从(s)里上一个(1),一直到(i‘-k-1)的这段区间,全部吃掉。
以上是已经确定(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;
}
以上是关于题解 CF1383E Strange Operation的主要内容,如果未能解决你的问题,请参考以下文章
CodeForces 1380F Strange Addition 题解
CF 1529B. Sifid and Strange Subsequences