首页 > 其他分享 >DP 专训

DP 专训

时间:2022-11-22 23:12:32浏览次数:37  
标签:ch int sum times DP 专训 binom dp

因为一些原因前面部分先咕着

string Counting

题意

\(~~~~\) 有 \(26\) 种不同的字符,第 \(i\) 种有 \(c_i\) 个。构造一个长为 \(n\) 的字符串,使得字符串上不存在长度为奇数且大于 \(1\) 的回文串的方案数。
\(~~~~\) \(3\leq n\leq 400,\frac{n}{3}<c_i\leq n\).

题解

\(~~~~\) 实质就是第 \(i\) 位不与 \(i+2\) 位相同。也就是分成奇数和偶数位,前 \(\lfloor \frac{n}{2}\rfloor+1\) 位相邻的不同,后面的相邻的不同。

\(~~~~\) 注意到如果不存在限制的话,那其实很容易做。由上面的转化可知答案为 \(26^2\times 25^{n-2}\).

\(~~~~\) 然后呢?然后假设我们注意到奇异的 \(\frac{n}{3}<c_i\leq n\) 的限制,那就会发现最多有两个字符是超限的,所以直接考虑容斥做。

\(~~~~\) 所以我们要求出至少一个字符超限的方案数和至少两个字符超限的方案数,先考虑前者:

\(~~~~\) \(f_{i,j,k}\) 表示前 \(i\) 位,共用了 \(j\) 个钦定字符,且当前用的是否为钦定字符。

\(~~~~\) 那么有转移:

\[dp_{i,j,0}=\left\{\begin{array}{l} dp_{i-1,j,0}\times 24+dp_{i-1,j,1}\times 25~~~~i\not=\lfloor \frac{n}{2} \rfloor+1\\ dp_{i-1,j,0}\times 25+dp_{i-1,j,1}\times 25~~~~i = \lfloor \frac{n}{2} \rfloor+1 \end{array}\right.\]

\[dp_{i,j,1}=\left\{\begin{array}{l} dp_{i-1,j,0}~~~~i\not=\lfloor \frac{n}{2} \rfloor+1\\ dp_{i-1,j,0}+dp_{i-1,j,1}~~~~i = \lfloor \frac{n}{2} \rfloor+1 \end{array}\right.\]

\(~~~~\) 求完过后答案就是 \(\sum_{i=1}^{26} \sum_{j=c_i+1}^n dp_{i,j,0}+dp_{i,j,1}\)

\(~~~~\) 同理可以定义另一个 \(dp\):\(f_{i,j,k,l}\) 表示两种字符分别用了 \(j\) 和 \(k\) 次,且当前位不是钦定字符,钦定字符1,钦定字符2。转移与上面类似,答案也类似,但注意求的时候要考虑两个钦定的字符不能重复。(对有傻子没有考虑。)

代码

查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
const int MOD=998244353;
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1) ret=Mul(ret,a);
		b>>=1;a=Mul(a,a);
	}
	return ret;
} 
int f[405][405][2],g[405][405][405][3],Lim[405]; 
int main() {
	int n;read(n);
	for(int i=1;i<=26;i++) read(Lim[i]);
	int Ans1=Mul(qpow(26,2),qpow(25,n-2)),Ans2=0,Ans3=0;
	f[1][1][1]=1;f[1][0][0]=25;
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<=n;j++)
		{
			if(i!=n/2+1)
			{
				if(j) f[i][j][1]=f[i-1][j-1][0];
				f[i][j][0]=Add(Mul(24,f[i-1][j][0]),Mul(25,f[i-1][j][1]));
			}
			else
			{
				if(j) f[i][j][1]=Add(f[i-1][j-1][0],f[i-1][j-1][1]);
				f[i][j][0]=Add(Mul(f[i-1][j][0],25),Mul(f[i-1][j][1],25));	
			}
		}
	}
	for(int i=1;i<=26;i++) for(int j=Lim[i]+1;j<=n;j++) Ans2=Add(Ans2,Add(f[n][j][1],f[n][j][0]));
	g[1][0][0][0]=24; g[1][1][0][1]=g[1][0][1][2]=1;
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<=i;j++)
		{
			for(int k=0;j+k<=i;k++)
			{
				if(i!=n/2+1)
				{
					if(j) g[i][j][k][1]=Add(g[i-1][j-1][k][0],g[i-1][j-1][k][2]);
					if(k) g[i][j][k][2]=Add(g[i-1][j][k-1][0],g[i-1][j][k-1][1]);
					g[i][j][k][0]=Add(Mul(g[i-1][j][k][0],23),Add(Mul(g[i-1][j][k][1],24),Mul(g[i-1][j][k][2],24)));
				}
				else
				{
					if(j) g[i][j][k][1]=Add(g[i-1][j-1][k][0],Add(g[i-1][j-1][k][1],g[i-1][j-1][k][2]));
					if(k) g[i][j][k][2]=Add(g[i-1][j][k-1][0],Add(g[i-1][j][k-1][1],g[i-1][j][k-1][2]));
					g[i][j][k][0]=Add(Mul(g[i-1][j][k][0],24),Add(Mul(g[i-1][j][k][1],24),Mul(g[i-1][j][k][2],24)));
				}
			}
		}
	}
	for(int i=1;i<=26;i++)
		for(int j=i+1;j<=26;j++)
			for(int k=Lim[i]+1;k<=n;k++)
				for(int l=Lim[j]+1;k+l<=n;l++) Ans3=Add(Ans3,Add(g[n][k][l][0],Add(g[n][k][l][1],g[n][k][l][2])));
			
	printf("%d",Add(Dec(Ans1,Ans2),Ans3));
	return 0;
}
/*
不会吧?26^3 * n 先完成没有限制的做法
甚至好像可以组合数学 26^2*25^{n-2} 
注意到会超限的字符不多,应该是两个以内? 
容斥掉就好了?
对于一个超限的情况:
	记 dp_{i,j,k} 表示前 i 个字符,一共用了 j 个超限字符,并且是/不是超限字符
	dp_{i,j,1}=dp_{i-1,j-1,0} / dp_{i-1,j-1,0}+dp_{i-1,j-1,1} (i=n/2+1时)
	dp_{i,j,0}=24*dp_{i-1,j,0}+25*dp_{i-1,j,1} / 25*dp_{i-1,j,0}+25*dp_{i-1,j,1} 
	(我们认为是前一半相邻不能相同,后一半相邻不能相同)
对于两个超限的情况:
	记 dp_{i,j,k,l,m} 表示`···j个超限字符 1,k个超限字符2,l表示上一位是普通/字符1/字符2的情况 
	转移相似 
*/

Escape Through Leaf

题意

\(~~~~\) 给一棵根为 \(1\) 的树,第 \(i\) 个点上有两个值 \(a_i\) 和 \(b_i\)。允许从一个子树的根节点 \(u\) 跳到子树内任何一个非自身的点 \(v\),代价为 \(a_u\times b_v\),总的代价就是每次跳跃的代价和,求从每个点跳到一个叶子的最小代价和。
\(~~~~\) \(1\leq n\leq 10^5,-10^5\leq a_i,b_i\leq 10^5\)。

题解

\(~~~~\) 首先读对题,不要读成跳到儿子然后认为这是sb换根dp

\(~~~~\) 首先我们有一个简单的 dp:记 \(dp_i\) 表示第 \(i\) 个节点的答案,则 \(dp_{u}=dp_{v}+a_u\times b_v~~~~v\in \text{subtree(u)}\)。可以 \(\mathcal{O(n^2)}\) 求答案。

\(~~~~\) 然后来考虑一下 \(dp\) 怎么优化,注意到这个dp转移是一个一次函数的形式,所以把 \(dp_v\) 视作常数项,\(a_u\) 视作 \(x\) ,\(b_v\) 视作斜率,那不就是用李超数维护最小值吗。

\(~~~~\) 然后在树上怎么做?由于我不会李超树的线段树合并,所以这里选用 dsu on tree,多加一个 \(\log n\) 对这题影响不大。

\(~~~~\) 至于说线段树合并为什么是 \(n\log n\) 的复杂度证明我不会,并且也没写,但应该跟普通线段树合并大差不离。

\(~~~~\) 然后还有一个问题是,我写的代码没过CF (尴尬)

Beautiful Bracket Sequence

题意

\(~~~~\) 一个长为 \(n\) 的字符串,包含 (,),?,每个 ? 可替换为 () ,定义一个括号序列的权值为其最深的匹配深度(空串深度为 \(0\),形如 (s) 的串深度为 \(dep_s+1\),st的权值为两者的较大值)。求所有替换方案的权值和。对 \(998244353\) 取模。
\(1\leq n\leq 10^6,\text{Easy Version}:1\leq n\leq 2000\).

题解

\(~~~~\) Easy Version 是 有用 没用 有用的。

\(~~~~\) 如果Easy Version 像我一样用dp去想那就没用,但如果在dp专题里想组合数学(?)那就有用。

\(~~~~\) 怎么求权值?注意到,如果我们把一个已知的括号序列分成两部分,左边的左括号数为 \(l_i\),右边的右括号数为 \(r_i\) ,那么这个划分的答案就是 \(\min(l_i,r_i)\),感觉很显然。所以可以枚举划分点求出所有情况的 max

\(~~~~\) 那加入了 ? 怎么做呢?枚举现在的划分点和答案,但是 min 不好处理?

\(~~~~\) 发现 \(l\) 是递增的,\(r\) 是递减的,所以取得 \(\max\) 时一定有 \(l_i=r_i\) 的情况,所以认为两边都是答案是合理的。

\(~~~~\) 记当前分界点左边有 \(s_1\) 个(,\(s_2\) 个?,右边有 \(s_3\) 个 ) ,\(s_4\) 个 ?

\(~~~~\) 那答案就是:

\[\sum_{i=1}^n \sum_{j=0}^n j\times \binom{s_2}{j-s_1}\times \binom{s_4}{j-s_3} \]

\(~~~~\) 这样我们就完成了一个 \(\text{Easy Version}\).

\(~~~~\) 然后优化这个式子?组合意义天地灭!

\(~~~~\) 把 \(j\) 换成 \(s_1-s_1+j\),然后拆开成 \(s_1\) 和 \(j-s_1\)。

\(~~~~\) 对于拆出 \(s_1\) 的情况:

\[\sum_{i=1}^n \sum_{j=0}^n s_1\times \binom{s_2}{j-s_1}\times \binom{s_4}{j-s_3}\\ =\sum_{i=1}^n \sum_{j=0}^n s_1\times \binom{s_2}{j-s_1}\times \binom{s_4}{s_4-j+s_3} \]

\(~~~~\) 注意到后面两个数的组合意义连同求和表示从 \(s_2+s_4\) 个物品中选出 \(s_4+s_3-s_1\) 个,那也是直接组合数 \(\binom{s_2+s_4}{s_4+s_3-s_1}\) 上去就好了.

\(~~~~\) 对于拆出 \(j-s_1\) 的情况:

\[\sum_{i=1}^n \sum_{j=0}^n (j-s_1)\times \binom{s_2}{j-s_1}\times \binom{s_4}{j-s_3}\\ =\sum_{i=1}^n \sum_{j=0}^n (j-s_1)\times \frac{s_2!}{(j-s_1)!(s_2-j+s_1)!}\times \binom{s_4}{j-s_3}\\ =\sum_{i=1}^n \sum_{j=0}^n \frac{s_2!}{(j-s_1-1)!(s_2-j+s_1)!}\times \binom{s_4}{s_2-s_3}\\ =\sum_{i=1}^n \sum_{j=0}^n s_2\times \frac{(s_2-1)!}{(j-s_1-1)!(s_2-j+s_1)!}\times \binom{s_4}{j-s_3}\\ =\sum_{i=1}^n \sum_{j=0}^n s_2\times \binom{s_2-1}{j-s_1-1}\times \binom{s_4}{s_4-j+s_3} \]

\(~~~~\) 那还是用上面的套路,把后面部分换成 \(\binom{s_2+s_4-1}{s_4+s_3-s_1-1}\) 即可。

\(~~~~\) 然后两个部分换成组合数后就 \(\mathcal{O(n)}\) 做即可。

代码

查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
const int MOD=998244353;
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1) ret=Mul(ret,a);
		b>>=1;a=Mul(a,a);
	}
	return ret;
}
char S[2000005];
int Fac[2000005],Inv[2000005];
int PreL[2000005],PreR[2000005],PreQ[2000005];
inline int C(int n,int r){return n>=r?Mul(Fac[n],Mul(Inv[r],Inv[n-r])):0;}
int main() {
	Fac[0]=1;
	for(int i=1;i<=2000000;i++) Fac[i]=Mul(Fac[i-1],i);
	Inv[2000000]=qpow(Fac[2000000],MOD-2);
	for(int i=2000000-1;i>=0;i--) Inv[i]=Mul(Inv[i+1],i+1); 
	int n;
	scanf("%s",S+1); n=strlen(S+1); 
	for(int i=1;i<=n;i++)
	{
		PreL[i]=PreL[i-1]+(S[i]=='(');
		PreR[i]=PreR[i-1]+(S[i]==')');
		PreQ[i]=PreQ[i-1]+(S[i]=='?');
	}
	int Ans=0;
	for(int i=0;i<=n;i++)
	{
		int L=PreL[i],R=PreR[n]-PreR[i],Q1=PreQ[i],Q2=PreQ[n]-PreQ[i];
		Ans=Add(Ans,Mul(L,C(Q1+Q2,Q2+R-L)));
		Ans=Add(Ans,Mul(Q1,C(Q1+Q2-1,R+Q2-L-1)));
	}
	printf("%d",Ans);
	return 0;
}
/*
DP 专题,但是组合数学 
*/

标签:ch,int,sum,times,DP,专训,binom,dp
From: https://www.cnblogs.com/Azazel/p/16916839.html

相关文章

  • 152. 乘积最大子数组(经典dp)
    给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。测试用例的答案是一个 32-位 整数。子数组......
  • 600. 不含连续1的非负整数(数位dp或数学)
    (hard还是有点强度的)给定一个正整数 n ,请你统计在 [0,n] 范围的非负整数中,有多少个整数的二进制表示中不存在 连续的1 。 示例1:输入:n=5输出:5解释:......
  • ADPCM(自适应差分脉冲编码调制)的原理和计算
    关于ADPCMADPCM(AdaptiveDifferentialPulseCodeModulation,自适应差分脉冲编码调制)是一种音频信号数字化编码技术,音频压缩标准G.722,G.723,G.726中都会使用到......
  • 动态 DP
    例题:最大子段和问题,区间询问版本。https://www.luogu.com.cn/problem/SP1043考虑dp。\[dp_{l}=a_l\\dp_{i}=\max(dp_{i-1}+a_i,a_i)\]根据矩阵乘法的重新......
  • NFC芯片DP1363F兼容LRC663/RC522支持18092/15693协议多设备读卡超高性价比
    DP1363是一款高度集成的非接触读写芯片,集强大的多协议支持、最高射频输出功率、以及突破性技术低功耗卡片检测等优势一身,满足市场对更高集成度、更小外壳和互操作性的需求,......
  • 树形背包 / 树形DP(牛客 - 蓝魔法师)
    一般的树形背包问题,往往与以下模板异曲同工。复杂度为\(O(n^3)\)voiddfs(intu){ sz[u]=1; for(autov:G[u]) { dfs(v);sz[u]+=sz[v]; for(intj=sz[......
  • ffmep报错:无法定位程序输入点SetThreadDpiAwarenessContext
    ffmep报错:无法定位程序输入点SetThreadDpiAwarenessContext于......ffmep.exe过程:安装vs sdito2015安装微软常用运行库合集解决方式:换一个ffmep版本 钻研不易,转载......
  • 使用阿里云云服务器和Wordpress个人建站
    先贴一个阿里云官方的通过ECS服务器建站的教程链接https://developer.aliyun.com/article/761621需要说明的是如果需要通过公网访问你的网站那么必须要先进行I......
  • SAP日志表 CDHDR和CDPOS
    1.标准日志表CDHDR和 CDPOSOBJECTCLAS='INFOSATZ'信息记录OBJECTCLAS='BANF'采购申请OBJECTCLAS='EINKBELEG'采购订单2.可以通过表TCDOBT......
  • WordPress编辑器支持PowerPoint一键粘贴
    ​如何做到ueditor批量上传word图片?1、前端引用代码<!DOCTYPE html PUBLIC "-//W3C//DTDXHTML1.0Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-......