2025.3.26 数学专题

CF1264D2 Beautiful Bracket Sequence (hard version)

定义一个括号序列的“深度”为删去一些括号后匹配括号层数的最大值,现给定一个每一位为 \((\ )\ ?\) 的括号序列,\(?\) 既可以看作是 \((\) 也可以看作是 \()\),求所有可能的括号序列的深度之和 \(\bmod998244353\)

考虑删去一些括号之后最后可以直接统计深度的一定是形如 \(((\cdots()))\cdots)\) 的序列且左右括号数量相等,我们不妨直接统计序列中左边有用的左括号有多少个。那么枚举左右括号的分界点 \(t\),如果左边有 \(l\)\((\),右边有 \(r\)\()\),左边和右边分别有 \(x\)\(y\)\(?\)。如果我们在左边从 \(x\) 拿出来 \(i\)\(?\) 成为 \((\),一共有 \(l+i\) 个左括号,贡献即为 \(l+i\);在右边从 \(y\) 中拿出一部分和 \(r\) 一起凑出 \(l+i\) 个右括号,选择的 \(?\) 个数就是 \(l+i-r\),其实总贡献就可以表示为

\[ans=\sum_{t=1}^n\sum_{i=0}^x(l+i){x\choose i}{y\choose l+i-r} \]

每个位置的 \(l,r,x,y\) 是好处理的,所以可以每个位置单独计算;现在考虑中间那块组合数怎么快速计算。先拆贡献,有:

\[ans_t=l \sum_{i=0}^x{x\choose i}{y\choose l + i - r}+\sum_{i=0}^xi{x\choose i}{y\choose l + i - r} \]

对组合数进行基本的恒等变换,吸收公式 \(+\) 对称公式 \(+\) 范德蒙德卷积即可得到

\[\begin{aligned} ans_t&=l \sum_{i=0}^x{x\choose i}{y\choose l + i - r}+x\sum_{i=0}^x{x-1\choose i-1}{y\choose l+i-r}\\ &=l \sum_{i=0}^x{x\choose i}{y\choose y-l-i+r}+x\sum_{i=0}^x{x-1\choose i-1}{y\choose y-l-i+r}\\ &=l{x+y\choose y-l+r}+x{x+y-1\choose y-l+r-1}\\ \end{aligned} \]

预处理可以做到 \(O(n)\) 的时间复杂度。注意组合数可能有负指标,要稍微判一下。

代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 2e6 + 10, mo = 998244353;
int n, prel[maxn], prer[maxn], preq[maxn];
char c[maxn];
ll fac[maxn], ifac[maxn], ans;

ll qpow(ll x, ll y) {
	ll res = 1;
	while(y) {
		if(y & 1) (res *= x) %= mo;
		y >>= 1, (x *= x) %= mo;
	} return res;
}
void pre_calc() {
	fac[0] = ifac[0] = 1; for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mo;
	ifac[n] = qpow(fac[n], mo - 2); for(int i = n - 1; i >= 1; i--) ifac[i] = ifac[i + 1] * (i + 1) % mo;
	for(int i = 1; i <= n; i++) prel[i] = prel[i - 1] + (c[i] == '('), prer[i] = prer[i - 1] + (c[i] == ')'), preq[i] = preq[i - 1] + (c[i] == '?');
	return;
}
ll C(ll x, ll y) {return (x < y || x < 0 || y < 0) ? 0ll : (fac[x] * ifac[y] % mo * ifac[x - y] % mo);}

int main() {
//	ios :: sync_with_stdio(false); cin.tie(0); cout.tie(0);
	
	scanf("%s", c + 1); n = strlen(c + 1); pre_calc();
	for(int i = 1; i <= n; i++) {
		ll x = preq[i], y = preq[n] - preq[i], l = prel[i], r = prer[n] - prer[i]; 
		ans = (ans + l * C(x + y, y + r - l) % mo + x * C(x + y - 1, y - l + r - 1) % mo) % mo;
	}
	
	printf("%lld", ans);
	return 0;
}

其实 easy version 的 \(O(n^2)\) DP 挺有意思的,也简单说一下。

考虑区间 DP 如何不重不漏地计算总贡献。设 \(f_{i,j}\) 表示区间 \([i,j]\) 的总贡献,答案即为 \(f_{1,n}\)
考虑小区间如何转移到大区间。对于一个确定的括号序列,如果 \([i,j]\) 最左边是 \()\),那么新增加这一位不会有任何新匹配的括号,就不可能产生新的贡献,即有 \(f_{i,j}=f_{i+1,j}\);如果最右边是 \((\),同样不会产生任何贡献,即有 \(f_{i,j}=f_{i,j-1}\);如果最左边和最右边同时是 \((\)\()\),那么答案贡献就一定增加 \(1\),即 \(f_{i,j}=f_{i+1,j-1}+1\)。总转移:

\[f_{i,j}= \begin{cases} f_{i+1,j} & s_i=\ )\\ f_{i,j-1} & s_j=(\\ f_{i+1,j-1}+1 & s_i=(\ \wedge s_j=\ ) \end{cases} \]

考虑如何拓展到有 \(?\) 的非确定括号序列上,那不是直接同时转移上面的所有式子就好了?
*其实不然.考虑两边都是问号时,如果每个式子都转移,相当于统计了 \()\cdots)/(\)\()/(\cdots(\)\((\cdots)\)\()\cdots(\) 这种情况就被重复统计了。类似的,就算只有一个问号的 \()\cdots)/(\)\()/(\cdots(\) 或一个问号也没有的 \()\cdots(\),由于方案数是符合条件直接累加的,所以也重复转移了,要减去重复贡献 \(f_{i+1,j-1}\)
如果记 \([i+1,j-1]\)\(?\) 数量为 \(cntq\),由于 \(?\) 决定了确定括号序列的数量是 \(2^{cntq}\) ,一对合法括号获得的贡献就是 \(2^{cntq}\)。所以有转移

\[f_{i,j}=f_{i,j}+f_{i+1,j},\ while\ s_i\ne( \]

\[f_{i,j}=f_{i,j}+f_{i,j-1},\ while\ s_j\ne\ ) \]

\[f_{i,j}=f_{i,j}+f_{i+1,j-1}+2^{cntq},\ while\ s_i\ne\ )\wedge s_j\ne( \]

\[f_{i,j}=f_{i,j}-f_{i+1,j-1},\ while\ s_i\ne(\ \wedge s_j\ne\ ) \]

从小到大枚举区间长度和左右端点即可,复杂度 \(O(n^2)\)

代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 2e3 + 10, mo = 998244353;
int n, cntq[maxn];
char c[maxn];
ll f[maxn][maxn];

ll qpow(ll x, ll y) {
	ll res = 1;
	while(y) {
		if(y & 1) (res *= x) %= mo;
		(x *= x) %= mo, y >>= 1;
	} return res;
}

int main() {
//	ios :: sync_with_stdio(false); cin.tie(0); cout.tie(0);
	
	scanf("%s", c + 1); n = strlen(c + 1);
	for(int i = 1; i <= n; i++) cntq[i] = cntq[i - 1] + (c[i] == '?');
	
	for(int len = 2; len <= n; len++) {
		for(int i = 1; i <= n - len + 1; i++) {
			int j = i + len - 1;
			
			if(c[i] != '(') f[i][j] += f[i + 1][j];
			if(c[j] != ')') f[i][j] += f[i][j - 1];
			if(c[i] != ')' && c[j] != '(') f[i][j] += f[i + 1][j - 1] + qpow(2, cntq[j - 1] - cntq[i]);
			if(c[i] != '(' && c[j] != ')') f[i][j] -= f[i + 1][j - 1];
			((f[i][j] %= mo) += mo) %= mo;
		}
		
	} 
	
	printf("%lld", f[1][n]);
	
	return 0;
} 

P5572 简单的数论题

给定 \(n,m\),求

\[\sum_{i=1}^n\sum_{j=1}^m\varphi({\text{lcm}(i,j)\over \gcd(i,j)})\bmod23333 \]

\(T\le3e4\) 次询问,\(m\le n\le5e4\)

肯定是要推式子的

先拆开 \(\text {lcm}(i,j)\)

\[ans=\sum_{i=1}^n\sum_{j=1}^m\varphi({ij\over \gcd(i,j)^2}) \]

枚举 \(d=gcd(i,j)\),枚举 \(d\) 的倍数,消去分母的 \(d^2\)

\[\begin{aligned} ans&=\sum_{d=1}^n\sum_{i=1}^n\sum_{j=1}^m\varphi({ij\over d^2})[\gcd(i,j)=d] \\&=\sum_{d=1}^n\sum_{i=1}^{\lfloor{ n\over d}\rfloor}\sum_{j=1}^{\lfloor{ m\over d}\rfloor}\varphi(ij)[\gcd(i,j)=1] \end{aligned} \]

积性函数性质直接拆开,用 \(\mu\) 来刻画互质

\[\begin{aligned} ans&=\sum_{d=1}^n\sum_{i=1}^{\lfloor{ n\over d}\rfloor}\sum_{j=1}^{\lfloor{ m\over d}\rfloor}\varphi(i)\varphi(j)[\gcd(i,j)=1]\\ &=\sum_{d=1}^n\sum_{i=1}^{\lfloor{ n\over d}\rfloor}\sum_{j=1}^{\lfloor{ m\over d}\rfloor}\varphi(i)\varphi(j)\sum_{t\vert i,t\vert j}\mu(t)\\ \end{aligned} \]

交换求和次序,再枚举 \(t\) 的倍数化简限制

\[\begin{aligned} ans&=\sum_{d=1}^n\sum_{t=1}^{\lfloor{n\over d}\rfloor}\mu(t)\sum_{t\vert i}^{\lfloor{ n\over d}\rfloor}\varphi(i)\sum_{t\vert j}^{\lfloor{ m\over d}\rfloor}\varphi(j)\\ &=\sum_{d=1}^n\sum_{t=1}^{\lfloor{n\over d}\rfloor}\mu(t)\sum_{i=1}^{\lfloor{ n\over dt}\rfloor}\varphi(it)\sum_{j=1}^{\lfloor{ m\over dt}\rfloor}\varphi(jt)\\ \end{aligned} \]

这样搞还是没办法整除分块,先令 \(G(n,k)=\sum_{i=1}^n\varphi(ik)\),注意到所有的 \(G(n,k)\) 是可以 \(O(n\log n)\) 预处理的。
再令 \(T=dk\) 再简化一下

\[\begin{aligned} ans&=\sum_{d=1}^n\sum_{t=1}^{\lfloor{n\over d}\rfloor}\mu(t)G(\lfloor{n\over dt}\rfloor,t)G(\lfloor{m\over dt}\rfloor,t)\\ &=\sum_{T=1}^n\sum_{k\vert T}\mu(t)G(\lfloor{n\over T}\rfloor,k)G(\lfloor{m\over T}\rfloor,k)\\ \end{aligned} \]

其实还是没法整除分块,再设 \(R(n,m,t)=\sum_{t\vert T}\mu(t)G(\lfloor{n\over T}\rfloor,t)G(\lfloor{m\over T}\rfloor,t)\)

考虑枚举三维的 \(n,m,t\) 预处理 \(\le \sqrt L\)\(R\),算 \(t\) 时枚举是调和级数,所以这一部分时间复杂度 \(O(L\sqrt L\log\sqrt L)\)
于是是剩下的 \(n\ge\sqrt L\)\(m\ge\sqrt L\) 就可以整除分块

posted @ 2025-03-26 17:01  Ydoc770  阅读(23)  评论(1)    收藏  举报