zoukankan      html  css  js  c++  java
  • AtCoder AGC046D Secret Passage (结论、DP)

    题目链接

    https://atcoder.jp/contests/agc046/tasks/agc046_d

    题解

    细节调了一万年系列。

    假设操作后长度为 (l),进行了 (n-l) 次操作。那么显然原串的最后 ((2l-n)) 个字符是最终的串的子序列,因为这些数根本没有动过。
    那么最终串剩下的数全都由前面删除的数插入而来,所以还有一个条件是存在一种将前面的数经过 ((n-l)) 次操作之后删除的 (0)(1) 的个数恰好与最终串相对应的方案。
    容易证明这两个条件就足够了。第一个条件显然可以设 (f[i][j][k]) 表示长度为 (i) 的串,一共 (j)0,从后往前匹配到原串第 (k) 位的方案数。
    然而重点是需要仔细考虑一下第二个条件的细节。
    首先我们需要一个 DP 来求这个东西。设 (dp[i][j][k]) 表示当前操作了原串前 (i) 位,一共删除了 (j)0(k)1 的方案数。转移详见代码。
    然后考虑什么样的 (f) 可以加进答案。我们的要求是进行 ((n-l)) 次操作,最后 ((2l-n)) 位不能动,对于一个 (f[i][j][k]) 来讲,原串中可以保留的位置是 ([k,n]), 或者 ([k+1,n]),..., 或者 ([lim,n]),其中 (lim=min(n+1,n-(2l-n)+1)). 若保留位置是 ([p,n]),那就意味着操作进行到 (p-1) 这个点。所以 (f[i][j][k]) 累加进答案的充要条件是存在一个 (kle ple lim) 使得 (dp[p-1][tot_0-j][tot_1-(i-j)]=1),即“操作到前 ((k-1)) 位,删掉 (0)(1) 的个数恰好满足 (i,j) 的要求”是可行的。

    时间复杂度 (O(n^3)).

    试错了无数次,还看了题解,血的教训/ll

    代码

    #include<bits/stdc++.h>
    #define llong long long
    #define mkpr make_pair
    #define y0 asdfiaewoet
    #define y1 dawurpoewiu
    #define tm paisjropsdj
    #define x first
    #define y second
    #define iter iterator
    #define riter reverse_iterator
    #define pll pair<llong,llong>
    using namespace std;
    
    inline int read()
    {
    	int x = 0,f = 1; char ch = getchar();
    	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') {f = -1;}}
    	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    	return x*f;
    }
    
    const int mxN = 300;
    const int P = 998244353;
    int n; char a[mxN+3];
    llong f[mxN+3][mxN+3][mxN+3];
    int cnt[mxN+3][2]; bool dp[mxN+3][mxN+3][mxN+3],can[mxN+3][mxN+3];
    
    void updsum(llong &x,llong y) {x += (x+y>=P?y-P:y);}
    
    int main()
    {
    	scanf("%s",a+1); n = strlen(a+1); for(int i=1; i<=n; i++) a[i] -= 48;
    	for(int i=1; i<=n; i++) {cnt[i][0] = cnt[i-1][0],cnt[i][1] = cnt[i-1][1]; cnt[i][a[i]]++;}
    	dp[0][0][0] = true;
    	for(int i=0; i<=n; i++)
    	{
    		for(int j=0; j<=i; j++) for(int k=0; k+j<=i; k++) if(dp[i][j][k])
    		{
    			//if "dp[i+1][j][k] = true;" then you are wrong
    			if(cnt[i][0]-j>=2) {dp[i][j+1][k] = true;}
    			if(cnt[i][1]-k>=2) {dp[i][j][k+1] = true;}
    			if(cnt[i][0]-j>=1&&cnt[i][1]-k>=1) {dp[i][j+1][k] = true,dp[i][j][k+1] = true;}
    			if(i+1<=n&&cnt[i][0]-j>=1) {dp[i+1][j+(a[i+1]==0)][k+(a[i+1]==1)] = true,dp[i+1][j+1][k] = true;}
    			if(i+1<=n&&cnt[i][1]-k>=1) {dp[i+1][j+(a[i+1]==0)][k+(a[i+1]==1)] = true,dp[i+1][j][k+1] = true;}
    			if(i+2<=n) {dp[i+2][j+(a[i+2]==0)][k+(a[i+2]==1)] = true,dp[i+2][j+(a[i+1]==0)][k+(a[i+1]==1)] = true;}
    		}
    	}
    	f[0][0][n+1] = 1ll;
    	for(int i=0; i<=n; i++)
    	{
    		for(int j=0; j<=i; j++)
    		{
    			for(int k=n+1; k>=n-i+1; k--)
    			{
    				updsum(f[i+1][j+1][k-(a[k-1]==0)],f[i][j][k]);
    				updsum(f[i+1][j][k-(a[k-1]==1)],f[i][j][k]);
    			}
    		}
    	}
    	llong ans = 0ll;
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=0; j<=i; j++) if(j<=cnt[n][0]&&i-j<=cnt[n][1])
    		{
    			int lim = min(n+1,2*n-2*i+1); bool ok = false;
    			for(int k=lim; k>=n-i+1; k--)
    			{
    				ok |= dp[k-1][cnt[n][0]-j][cnt[n][1]-(i-j)]; if(!ok) continue;
    //				printf("f[%d][%d][%d]=%lld
    ",i,j,k,f[i][j][k]);
    				updsum(ans,f[i][j][k]);
    			}
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Error Correct System(模拟)
    Pasha and String(思维,技巧)
    Vitaliy and Pie(模拟)
    Old Sorting(转化成单调序列的最小次数,置换群思想)
    Segment(技巧 相乘转换成相加 + java)
    Conquering Keokradong && Get the Containers(二分)
    Handshakes(思维) 2016(暴力)
    Dice Notation(模拟)
    “玲珑杯”郑州轻工业学院第八届ACM程序设计大赛暨河南高校邀请赛-正式赛(总结)
    MySQL安装-二进制软件包安装
  • 原文地址:https://www.cnblogs.com/suncongbo/p/14674621.html
Copyright © 2011-2022 走看看