首页 > 其他分享 >[AGC060D] Same Descent Set

[AGC060D] Same Descent Set

时间:2022-12-27 21:00:25浏览次数:61  
标签:cnt Set Descent limits int sum cap AGC060D dp

题解

考虑给定一个由 <> 组成的长度为 \(n-1\) 的字符串,第 \(i\) 位为 < 表示 \(p_i<p_{i+1}\) ,否则表示 \(p_i>p_{i+1}\) 。

假设有一个这样的字符串 \(t\) ,那么设 \(cnt(t)\) 表示满足 \(t\) 限制的排列的数量,那么题目所求即为 $\sum\limits_{s} cnt(t)^2 $ 。

考虑如何计算 \(cnt(t)\) 。假设字符串 \(t\) 中 \(S\) 这个集合的位置是 > ,对这些位置是否满足限制进行容斥,就可以转化为以下问题:

设 \(f(x)\) 为满足 \(\forall i\notin x,\ p_i<p_{i+1}\) 的排列 \(p\) 的数量 ,那么枚举字符串 \(t\) 中为 > 的集合 \(S\) ,有 \(cnt(t)=\sum\limits_{a\subseteq S} f(a)*(-1)^{|a|}\) ,答案即为 \(\sum\limits_S \sum\limits_{a,b\subseteq S} f(a)*f(b)*(-1)^{|a|+|b|}\) 。

先来看看 \(f(x)\) 怎么求。将一个长为 \(n\) 的序列在所有 \(i\in x\) 的位置断开,假设断成了 \(k\) 段,长度为 \(d_1,d_2,\cdots d_k\) ,那么方案数就是 \(\dfrac{n!}{d_1!d_2!\cdots d_k!}\) 。(式1)

改变求和顺序,考虑对于一对 \(a,b\) 有多少可以选的 \(S\) ,答案为 \(\sum\limits_{a,b} f(a)*f(b)*(-1)^{|a|+|b|}*2^{n-1-|a|-|b|+|a\cap b|}\) 。

比较难处理的就是这个 \(2^{|a\cap b|}\) 的系数,注意到 \((a\cap b)\) 恰好有 \(2^{|a\cap b|}\) 个子集,问题转化为求:

\(\sum\limits_c (\sum\limits_{c\subseteq a} f(a)/(-2)^{|a|})^2\) 。(式2)

那么注意到在所有 \(i\in c\) 的位置一定是断开了的,上面我们计算 \(f(x)\) 的式子告诉我们断开的两段之间的贡献是独立的,这启发我们使用一个 dp 来逐位确定 \(c\) 以及此时的贡献。

设 \(dp[i]\) 表示当前考虑了前 \(i\) 位,并且 \(c\) 中的最后一个元素恰为 \(i\) 时的答案。那么 \(dp[i]=\sum\limits_{j<i}dp[j]*g(i-j)^2/4\) 。除以 \(4\) 是 (式2) 中那个 \(((-2)^{|a|})^2\) 的贡献。

(式2) 中的 \(a\) 可以任意包含 \([j+1,i-1]\) 这一段中的元素,为此我们需要计算出 \(g(x)\) 表示任意划分长为 \(x\) 的段的贡献。\(g(x)\) 同样可以通过递推来得到:\(g(i)=\sum\limits_{j<i}g(j)/(-2)/(i-j)!\) ,这样就计算到了 (式1) 中分母的贡献以及 (式2) 中 \((-2)^{|a|}\) 的贡献。

合理设置 \(dp[0]\) 和 \(g(0)\) 的边界值,最后 \(dp[n]\) 即为所求。当然这还不是最终答案,需要将之前没有乘上的 \((n!)^2*2^{n-1}\) 给乘上。

\(g\) 的计算显然可以通过多项式求逆或者直接分治NTT求出。求出 \(g\) 后 \(dp\) 也可同理求出。

时间复杂度 \(O(n\log n)\) 或 \(O(n\log^2 n)\) 。

代码

#include <bits/stdc++.h>
#define N 800005
using namespace std;

const int mod = 998244353, inv2 = 499122177;
inline int qmod(int x) { return x<mod?x:x-mod; }
inline int fpow(int x, int t) { int r=1;for(;t;t>>=1,x=1ll*x*x%mod)if(t&1)r=1ll*r*x%mod;return r; }

int w[(1<<20)+5], rev[N], lim;
int fac[N], Inv[N], A[N<<2], B[N<<2];
int f[N], g[N], h[N];

void initMath() {
	w[0] = 1;
	for (int i = 1; i < (1<<20); i<<=1) {
		int wn = fpow(3,(mod-1)/(i<<1));
		w[i] = 1; for (int k = 1; k < i; k++) w[i+k] = 1ll*w[i+k-1]*wn%mod;
	}
	fac[0] = Inv[0] = 1;
	for (int i = 1; i <= N-5; i++) fac[i] = 1ll*fac[i-1]*i%mod;
	Inv[N-5] = fpow(fac[N-5],mod-2);
	for (int i = N-6; i; i--) Inv[i] = 1ll*Inv[i+1]*(i+1)%mod; 
}

void init(int mx) {
	int l = 0; lim = 1;
	while (lim < mx) lim <<= 1, ++l;
	for (int i = 0; i < lim; i++) rev[i] = (rev[i>>1]>>1)|((i&1)<<(l-1));
}
void NTT(int *F, int tp) {
	for (int i = 0; i < lim; i++) if (i < rev[i]) swap(F[i], F[rev[i]]);
	for (int i = 1; i < lim; i<<=1) {
		for (int j = 0; j < lim; j+=i+i) {
			for (int k = 0; k < i; k++) {
				int x = F[j+k], y = 1ll*F[i+j+k]*w[i+k]%mod;
				F[j+k] = qmod(x+y); F[i+j+k] = qmod(x-y+mod);
			}
		}
	}
	if (tp == -1) {
		int I = fpow(lim, mod-2); 
		reverse(F+1, F+lim);
		for (int i = 0; i < lim; i++) F[i] = 1ll*F[i]*I%mod;
	}
}
void Mul(int p, int q) {
	init(p+q); NTT(A, 1); NTT(B, 1);
	for (int i = 0; i < lim; i++) A[i] = 1ll*A[i]*B[i]%mod;
	NTT(A, -1);
}

void solve(int l, int r, int *_f, int *_g) {
	if (l == r) return;
	int mid = (l + r) >> 1;
	solve(l, mid, _f, _g);
	for (int i = l; i <= mid; i++) A[i-l] = _f[i];
	for (int i = 1; i <= r-l; i++) B[i-1] = _g[i];
	Mul(mid-l+1, r-l);
	for (int i = mid+1; i <= r; i++) _f[i] = qmod(_f[i]+A[i-l-1]);
	for (int i = 0; i < lim; i++) A[i] = B[i] = 0;
	solve(mid+1, r, _f, _g);
}

int main() {
	initMath();
	int n; scanf("%d", &n);
	for (int i = 1; i <= n; i++) h[i] = mod-1ll*Inv[i]%mod*inv2%mod;
	g[0] = mod-2; solve(0, n, g, h);
	int inv4 = fpow(4,mod-2);
	for (int i = 1; i <= n; i++) g[i] = 1ll*g[i]*g[i]%mod*inv4%mod;
	f[0] = 4; solve(0, n, f, g);
	int ans = 1ll*f[n]*fac[n]%mod*fac[n]%mod*fpow(2,n-1)%mod;
	printf("%d\n", ans);
	return 0;
}

标签:cnt,Set,Descent,limits,int,sum,cap,AGC060D,dp
From: https://www.cnblogs.com/ak-dream/p/17008987.html

相关文章

  • setjmp使用
    当 setjmp 和 longjmp 一起使用时,它们提供了一种执行非本地 goto 的方法。它们通常在C代码中用于将执行控制传递给先前调用的例程中的错误处理或恢复代码,而不使用......
  • SetProcessWorkingSetSize减少内存占用?啥也不是
    结论:别用这个函数,他会把内存写不下的写到硬盘的虚拟内存中去(注:硬盘中的虚拟内存默认在系统盘里)贴一段博客园名称pdfw的代码点击查看代码[System.Runtime.InteropServ......
  • Kubernetes StatefulSet 控制器(二十一)
    前面我们学习了Deployment和ReplicaSet两种资源对象得使用,在实际使用的过程中,Deployment并不能编排所有类型的应用,对无状态服务编排是非常容易的,但是对于有状态服务就......
  • Kubernetes DaemonSet 控制器(二十二)
    通过该控制器的名称我们可以看出它的用法:Daemon,就是用来部署守护进程的,DaemonSet用于在每个Kubernetes节点中将守护进程的副本作为后台进程运行,说白了就是在每个节点部署......
  • Kubernetes ReplicaSet 控制器(十九)
    前面我们一起学习了Pod的原理和一些基本使用,但是在实际使用的时候并不会直接使用Pod,而是会使用各种控制器来满足我们的需求,Kubernetes中运行了一系列控制器来确保集群......
  • Vue3之setup的两个注意点
    setup的两个注意点setup执行的时机在beforeCreate之前执行一次,this是undefined。setup的参数props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。......
  • PyTorch的Dataset 和TorchData API的比较
    深度神经网络需要很长时间来训练。训练速度受模型的复杂性、批大小、GPU、训练数据集的大小等因素的影响。在PyTorch中,torch.utils.data.Dataset和torch.utils.data.DataL......
  • Wix Setting language and code page attributes
    WhenyoulocalizeyourMSIpackage,you'llneedtoalteryourProductandPackageelementstosuit.Todoso,you'llleveragecodepagesandlocaleidentifiers......
  • 封装setStorage、getStorage
    /***存储localStorage*/exportconstsetStore=(params:any)=>{const{name,content,type,datetime}=paramsconstobj={dataType:t......
  • ef6不支持sqlserver 2008的offset语句分页解决
    一、问题项目中如果使用了EF6,数据库是2008,而分页会收到类似如下的错误提示:{"Depth":0,"ClassName":"","Message":"Incorrectsyntaxnear'OFFSET'.\r\nIn......