zoukankan      html  css  js  c++  java
  • 题解 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;
    }
    
  • 相关阅读:
    根据View找控制器
    ScrollView双击图片定点放大
    iOS消息推送原理和实现总结
    iOS完整学习路线图
    获取设备版本
    UIView与CALayer的区别,很详细
    iOS开发网络数据之AFNetworking使用
    (已实践)PLSQL本地还原Oracle数据库dmp文件
    所遇Oracle错误代码
    Dapper基本使用
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13376474.html
Copyright © 2011-2022 走看看