首页 > 其他分享 >p7914-solution

p7914-solution

时间:2024-03-04 20:59:44浏览次数:35  
标签:dp2 dp1 sum solution p7914 gets displaystyle dp

P7914 Solution

link

先考虑 Subtask \(4\)。设 \(dp_i\) 表示长度为 \(i\) 的方案数,按题目定义转移:

  • AB,ASB:\(\displaystyle dp_n\gets dp_n+\sum_{i=1}^{n-1}\sum_{j=0}^kdp_i\times dp_{n-i-j}\)

  • (A):\(\displaystyle dp_n\gets dp_n+dp_{n-2}\)

  • (SA),(AS):\(\displaystyle dp_n\gets dp_n+2\sum_{i=1}^kdp_{n-k-2}\)

初始化:\(\forall i\in[2,k+2],dp_i\gets1\)。

但是我们注意到形如 ()()()()*()*()(**)*()*(**)*(*) 的方案会被算重。

我们发现这些被算重的方案由多个小块加上中间的若干 * (或没有 *)拼成,其中 这些小块都由 (A),(AS),(SA) 转移得来

那么现在去重就很简单了,例如我们将 ()()() 唯一表示为 ()() + ()()*()*() 唯一表示为 ()*() + * + ()(**)*()*(**)*(*) 唯一表示为 (**)*()*(**) + * + (*),等等。

设 \(dp1_i\) 表示长度为 \(i\),由 (A),(AS),(SA) 转移得来的方案数,\(dp2_i\) 表示长度为 \(i\) 的总方案数,修改转移方程如下:

  • AB,ASB:\(\displaystyle dp2_n\gets dp2_n+\sum_{i=1}^{n-1}\sum_{j=0}^kdp2_i\times dp1_{n-i-j}\)

  • (A):\(\displaystyle dp1_n\gets dp1_n+dp2_{n-2}\)

\(\displaystyle dp2_n\gets dp2_n+dp2_{n-2}\)

  • (SA),(AS):\(\displaystyle dp1_n\gets dp1_n+2\sum_{i=1}^kdp2_{n-k-2}\)

\(\displaystyle dp2_n\gets dp2_n+2\sum_{i=1}^kdp2_{n-k-2}\)

初始化:\(\forall i\in[2,k+2],dp1_i\gets1,dp2_i\gets1\)。

现在来想想 \(\mathcal O(n^4)\) 的做法:我们发现我们的转移涉及两段区间的拼凑,于是很自然地想到了区间 dp。

设 \(dp1_{l,r}\) 表示在 \([l,r]\) 中由 (A),(AS),(SA) 转移得来的方案数,\(dp2_{l,r}\) 表示在 \([l,r]\) 中的总方案数:

(设 \(st_{l,r}\) 表示 \([l,r]\) 中的字符是否全都是 *?

  • AB,ASB:\(\displaystyle dp2_{l,r}\gets dp2_{l,r}+\sum_{i=l}^{r-1}\sum_{j=0}^kst_{i+1,i+j}\times dp2_{l,i}\times dp1_{i+j+1,r}\)

  • (A):\(\displaystyle dp1_{l,r}\gets dp1_{l,r}+dp2_{l+1,r-1}\)

\(\displaystyle dp2_{l,r}\gets dp2_{l,r}+dp2_{l+1,r-1}\)

  • (SA),(AS):\(\displaystyle dp1_{l,r}\gets dp1_{l,r}+\sum_{i=1}^k(st_{l+1,l+i}\times dp2_{l+i+1,r-1}+st_{r-i,r-1}\times dp2_{l+1,r-i-1})\)

\(\displaystyle dp2_{l,r}\gets dp2_{l,r}+\sum_{i=1}^k(st_{l+1,l+i}\times dp2_{l+i+1,r-1}+st_{r-i,r-1}\times dp2_{l+1,r-i-1})\)

初始化:\(\forall r-l\le k+2\),若 \(s_l\) 为 (*,\(s_r\) 为 )* 且 \(st_{l+1,r-1}=1\) 则 \(dp_{l,r}\gets 1\)。

因为 \(st\) 可以低于 \(\mathcal O(n^3)\) 预处理,这个算法瓶颈在于 ASB 的转移。我们考虑如何优化:

首先枚举第一个断点也就是 A 的右端点 \(i\),这个时候我们发现第二个断点(B 的左端点)仅有可能在 \(i+1\sim r_{i+1}+1\) 中取,其中 \(r_i\) 表示 \(i\) 往右最远的点满足 \([i,r_i]\) 中的字符全都是 *?

那么可以考虑对 \(dp1\) 做前缀和:设 \(\displaystyle sum_{l,r}=\sum_{i=1}^l dp1_{i,r}\),这样 ASB 转移方程就变成了:

\(\displaystyle dp2_{l,r}\gets dp2_{l,r}+\sum_{i=l}^{r-1}dp2_{l,i}\times(sum_{\min(r_{i+1}+1,r),r}-sum_{i,r})\)

复杂度 \(\mathcal O(n)\),最后的复杂度 \(\mathcal O(n^3)\)。

代码

#include <bits/stdc++.h>
#define LL long long
#define sl(s) strlen(s)
#define endline puts("")
#define pii pair<int , int>
#define pr_q priority_queue
#define DEBUG puts("DEBUG.")
using namespace std;
const int N = 5e2 + 10;
const int inf = ~0u >> 2;
const int p = 1e9 + 7;
int n,k,dp1[N][N],dp2[N][N]; // independent, dependent
int sum[N][N],r[N];
bool st[N][N]; // all '*' or '?'
char s[N];
int main()
{
//	freopen("bracket.in" , "r" , stdin);
//	freopen("bracket.out" , "w" , stdout);
	cin >> n >> k;
	scanf("%s" , s + 1);
	for(int i = 1;i <= n;i++)
	{
		for(int j = i;j <= n;j++)
		{
			bool flag = 0;
			for(int c = i;c <= j;c++)
				if(s[c] != '*' && s[c] != '?')
				{
					flag = 1;
					break;
				}
			st[i][j] = !flag;
		}
		r[i] = i - 1;
		for(int j = i;j <= min(n , i + k - 1);j++)
			if(s[j] == '*' || s[j] == '?')
				r[i] = j;
			else
				break;
	}
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= i - 1;j++)
			st[i][j] = 1;
	for(int len = 2;len <= k + 2;len++)
		for(int i = 1;i + len - 1 <= n;i++)
		{
			int j = i + len - 1;
			bool flag = !st[i + 1][j - 1] || s[i] == '*' || s[i] == ')' || s[j] == '*' || s[j] == '(';
			dp1[i][j] = dp2[i][j] = !flag;
			if(len < 4)
				for(int c = i;c <= j;c++)
					sum[c][j] = ( sum[c][j] + dp1[i][j] ) % p;
//			cout << flag << endl;
//			cout << j << " " << j + i + 1 << " " << dp1[j][j + i + 1] << " " << dp2[j][j + i + 1] << endl;
		}
	for(int len = 4;len <= n;len++)
		for(int i = 1;i + len - 1 <= n;i++)
		{
			int j = i + len - 1;
			if(s[i] == '*' || s[j] == '*')
				continue;
			for(int c = i;c <= j - 2;c++)
				if(r[c + 1] + 1 > j)
					dp2[i][j] = (dp2[i][j] + 1ll * dp2[i][c] * (sum[j][j] - sum[c][j] + p) % p) % p;
				else
					dp2[i][j] = (dp2[i][j] + 1ll * dp2[i][c] * (sum[r[c + 1] + 1][j] - sum[c][j] + p) % p) % p;
//				if( st[c + 1][c + d] )
//					dp2[i][j] = (dp2[i][j] + 1ll * dp2[i][c] * dp1[c + d + 1][j] % p) % p;
			if( (s[i] == '(' || s[i] == '?') && (s[j] == ')' || s[j] == '?') )
			{
				dp1[i][j] = ( dp2[i + 1][j - 1] + dp1[i][j] ) % p,dp2[i][j] = ( dp2[i + 1][j - 1] + dp2[i][j] ) % p;
				for(int c = 1;c <= min(k , len - 3);c++)
				{
					if( st[j - c][j - 1] )
						dp1[i][j] = ( dp1[i][j] + dp2[i + 1][j - 1 - c] ) % p,
						dp2[i][j] = ( dp2[i][j] + dp2[i + 1][j - 1 - c] ) % p;
					if( st[i + 1][i + c] )
						dp1[i][j] = ( dp1[i][j] + dp2[i + c + 1][j - 1] ) % p,
						dp2[i][j] = ( dp2[i][j] + dp2[i + c + 1][j - 1] ) % p;
				}
			}
			sum[i][j] = ( sum[i - 1][j] + dp1[i][j] ) % p;
//			cout << i << " " << j << " " << dp1[i][j] << " " << dp2[i][j] << endl;
		}
	cout << dp2[1][n] << endl;
	return 0;
}
/*
10 2
???(*??(?)
*/

标签:dp2,dp1,sum,solution,p7914,gets,displaystyle,dp
From: https://www.cnblogs.com/iorit/p/18052652

相关文章

  • p7913-solution
    P7913Solutionlink先考虑有\(n\)个廊桥的分配。假设廊桥编号为\(1\simn\),我们用两个堆\(h1,h2\)分别存当前空闲的廊桥编号和正在使用廊桥的飞机的离开时间。对于国内和国外的飞机分别做一次以下操作:先按到达时间排序,从左到右扫,到第\(i\)架飞机的到达、离开时间为\(l_......
  • p5384-solution
    P5384Solutionlink弱化这题空间\(\mathcalO(n\logn)\)会MLE。考虑怎么搞到\(\mathcalO(n)\)。首先求k级祖先用树剖空间是\(\mathcalO(n)\)的。然后看看我们建线段树的过程,我们发现每次查询都是在对应深度的线段树里查,那么考虑把询问离线,把节点、询问对应到深度......
  • p4845-solution
    P4845Solutionlink考虑树形dp,对于每个\(u\)直接钦定它的三种可能状态:有灯,没灯但是被其他灯照亮,没灯也没被照亮。这样钦定会导致一些需要被照亮的点在当前子树中还未被照亮,需要依靠子树外的灯来照亮。称这种点为闲置点。状态内只需要记录闲置点中最深的到子树根的距离,以及......
  • p4632-solution
    P4632Solutionlink对时间扫描线,就变成支持单点加入删除一个颜色点,求所有颜色距离某个点的距离最大值。考虑二分答案,现在就是要检验\([x-mid,x+mid]\)内是否有\(1\simk\)颜色的点各至少一个。数颜色可以考虑维护\(pre_i\)表示上一个与该点同色的位置。然后区间\([l,r]......
  • p4555-solution
    P4555Solutionlink双回文串的左右两半部分显然是互相独立的。于是考虑求出\(lm_i\)表示以\(i\)结尾的最长回文子串长度,\(rm_i\)表示以\(i\)开头的最长回文子串长度,最后扫一遍所有的分隔符求\(lm+rm\)的最大值即可。考虑如何求\(lm,rm\)。先用manacher拉出\(p\),......
  • Solution - 消息传递
    Link。首先我们假设在看本文章的所有人除了@左乐都已经使用点分树A掉了震波。然后我们要怎么简单修改A掉这个消息传递呢。震波是维护距离\(\leqk\)的点,这里只需要维护\(=k\)的,显然我们可以改掉维护前缀和的那个数据结构(比如树状数组),直接用普通数组/vector。跳......
  • p4137-solution
    P4137Solutionlink考虑建主席树:权值线段树的叶子维护这个权值最后出现的下标,push_up的时候取\(\min\)。这样一个区间的\(\min\)小于\(k\)意味着有一个权值最后出现的下标小于\(k\),也就是说\(k\)后面没有出现这个权值。也就是说mex就在这个区间内。这样询问的时候......
  • p3990-solution
    P3990Solutionlink一次只能跳一步的情况下:\(dp_{i,j}=dp_{i-1,j-1}+dp_{i-1,j}+dp_{i-1,j+1}\)接下来考虑能跳奇数步:你发现跳\(3\)步相当于先跳一个奇数\(1\)再跳一个\(2\),跳\(5\)步相当于先跳一个奇数\(3\)再跳一个\(2\)也就是说能够一步跳到\((i,j)\)的一定能......
  • p3773-solution
    P3773Solutionlink\[\binomnm\bmod2=\binom{n\bmod2}{m\bmod2}\binom{n/2}{m/2}\bmod2\]我们要让\(\binomnm\bmod2\)不为\(0\),也就是让右式的两项均不为\(0\)。考虑\(\binom{n\bmod2}{m\bmod2}\):\(n\bmod2\)和\(m\bmod2\)的取值要么是\(0\)要么是\(1\)......
  • p3768-solution
    P3768Solutionlink\(\begin{aligned}\sum_{i=1}^n\sum_{j=1}^nij\gcd(i,j)&=\sum_{d=1}^n\sum_{i=1}^n\sum_{j=1}^nijd[\gcd(i,j)=d]\\&=\sum_{d=1}^nd^3\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}ij[......