• Atcoder Grand Contest 022 E Median Replace(dp)


    Atcoder 题面传送门 & 洛谷题面传送门

    首先考虑对于固定的 01 串怎样计算它是否可以通过将三个连续的 \(0\)\(1\) 替换为其中位数得到。我们考虑单调栈,新建一个栈,栈底到栈顶分别是一段连续的 \(1\) 和一段连续的 \(0\),分别表示当前未合并完的字符,我们从左往右扫描,每遇到一个字符可分以下两种情况:

    • 如果我们遇到一个 \(0\)

      • 如果栈为空,那直接将这个 \(0\) 入栈就好了。

      • 如果栈顶 \(0\) 的个数 \(\ge 2\),那么显然我们可以把三个连续的 \(0\) 合并成一个 \(0\),因为不论我们后来加入什么样的数字,这样一一合并下去最终只可能得到一个 \(0\),也就是说原来栈顶的两个 \(0\) 变成了一个 \(0\)

      • 如果栈顶没有 \(0\),否则直接将这个 \(0\) 入栈。

    • 如果我们遇到一个 \(1\)

      • 如果栈为空,那直接将这个 \(1\) 入栈就好了。

      • 如果栈顶有 \(0\),并且栈不为空,那么栈最顶上的两个元素一定是一个 \(0\) 一个 \(1\),而显然如果第一个数是 \(0\),第二个数是 \(1\),那第三个数不论是多少,取中位数得到的结果一定还是第三个数,所以我们索性将最开头的 \(01\) 抵消,也就是说栈中 \(0\) 的个数减少了 \(1\),此时栈底到栈顶还是一段连续的 \(1\) 和一段连续的 \(0\)

      • 否则,栈中一定只剩一段连续的 \(1\)。如果这段 \(1\) 的个数 \(\ge 3\),仿照之前的推理过程它可以等效于 \(3\)\(1\),否则我们就直接将 \(1\) 入栈。

    显然最终栈不为空,并且该 01 串 \(s\) 满足条件的充要条件是最后 \(1\) 的个数大于 \(0\) 的个数,因为如果在合并过程中不存在栈中剩余 \(\ge 3\)\(1\),并且又进来了一个 \(1\) 的情况,那么每次操作栈内数的个数要么 \(+1\) 要么 \(-1\),而 \(n\) 为奇数,故最后栈中剩余数的个数是奇数。如果只剩一个 \(1\) 那就直接满足条件了,如果剩 \(2\)\(1\) 一个 \(0\) 或者 \(3\)\(1\) 把这三个数合并一下即可,如果剩 \(3\)\(1\) 两个 \(0\) 根据抽屉原理一定存在连续的三个数中恰好有 \(2\)\(1\),把这三个数合并得到一个 \(1\),再把剩下三个数合并即可。如果在合并过程中存在栈中剩余 \(\ge 3\)\(1\),并且又进来了一个 \(1\) 的情况,那么最终这三个 \(1\) 一定不会被消掉,也就是说最终 \(1\) 的个数为 \(3\),严格大于 \(0\) 的个数的最大值 \(2\),也符合题意。

    接下来考虑原题。不难发现任何时刻单调栈中 \(1\) 的个数 \(\le 3\)\(0\) 的个数 \(\le 2\),也就是说本质不同的单调栈的个数 \(\le 12\)。我们考虑将单调栈放入 DP 状态中,即设 \(dp_{i,x,y}\) 表示考虑到第 \(i\) 个字符,单调栈中还剩 \(x\)\(0\)\(y\)\(1\) 的方案数。分该位填 \(0\) 和该位填 \(1\) 转移即可。复杂度线性。

    const int MAXN=3e5;
    const int MOD=1e9+7;
    int n,dp[MAXN+5][3][4];char s[MAXN+5];
    void add(int &x,int v){((x+=v)>=MOD)&&(x-=MOD);}
    int main(){
    	scanf("%s",s+1);n=strlen(s+1);dp[0][0][0]=1;
    	for(int i=0;i<n;i++) for(int c0=0;c0<3;c0++) for(int c1=0;c1<4;c1++){
    		if(s[i+1]!='1'){//digit 0
    			if(c0==2) add(dp[i+1][1][c1],dp[i][c0][c1]);
    			else add(dp[i+1][c0+1][c1],dp[i][c0][c1]);
    		} if(s[i+1]!='0'){//digit 1
    			if(c0) add(dp[i+1][c0-1][c1],dp[i][c0][c1]);
    			else add(dp[i+1][c0][min(c1+1,3)],dp[i][c0][c1]);
    		}
    	} int ans=0;
    	for(int c1=1;c1<=3;c1++) for(int c0=0;c0<c1;c0++)
    		add(ans,dp[n][c0][c1]);
    	printf("%d\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    ubuntu Server 16.04 LTS 安装odoo
    linux常用命令大全
    sql 百万级数据库优化方案
    FreeSpire.XLS的使用
    备份集中的数据库与现有的数据库不同解决方案
    图片延迟加载的实现
    亚马逊菜单应用例子
    提取吗
    linux内核学习网站
    phpexcel1
  • 原文地址:https://www.cnblogs.com/ET2006/p/agc022E.html
Copyright © 2020-2023  润新知