P10004 [集训队互测 2023] Permutation Counting 2

这也太 hard 了,如何想到这个双射 /wq。

对于 \(P\) 恰好 \(i\) 个上升对,\(P^-\) 恰好 \(j\) 个上升对计数记作 \(f_{i,j}\)。不妨考虑计数 \(P\) 至少 \(i\) 个上升对,\(P^-\) 至少 \(j\) 个上升对 \(g_{i,j}\)

\[g_{i,j}=\sum_{x\ge i}\sum_{y\ge j}{x\choose i}{y\choose j}f_{x,y} \]

\[f_{i,j}=\sum_{x\ge i}\sum_{y\ge j}(-1)^{x+y-i-j}{x\choose i}{y\choose j}g_{x,y} \]

考虑计算 \(g_{i,j}\)

神仙。首先有 \(i\) 个上升对意味着有 \(n-1-i\) 个下降对,而每个下降对相当于新开一个上升段。也就是说 \(P\) 至少由 \(n-i\) 个上升段组成。每个上升段中相当于 \(P^-\) 中的 \([l,r]\) 依次分配到 \(p_i,p_{i+1},p_{i+2},\cdots\)。而每个上升段 \([l,r]\)\(p_i\) 递增 \(i\) 也递增,等价于向 \(P^-\) 中填入了一个上升序列。然后将 \(P^-\) 分成至少 \(j\) 个上升段的方案数。这个既跟 \(P\) 中上升段的情况有关,又跟填入的情况有关,计数又困又难。

若固定 \(P^-\) 中的上升段 \([l,r],[l_1,r_1],[l_2,r_2],\cdots\),要满足此条件即为段内排序。对于 \(P\) 相同的情况,这样影响 \(P^-\) 结果的条件就只有每个段内出现的数。

考虑填入第 \(i\) 个上升序列时,向 \(P^-\)\(j\) 个连续段填入了 \(c_{i,j}\) 个数。当所有的 \(c_{i,j}\) 定下来时,记 \(s_{i}\) 为前 \(i\) 个连续段向逆序列填入了多少数,\(P\) 的第 \(i\) 个上升段即为 \((s_{i-1},s_i]\)。而 \(P_i\) 等于 \(P^-\) 每段排好序后 \(i\) 所在的位置。也就是 \(c\) 固定后 \(P\)\(P^-\) 的情况均确定。

则两种方案不同当且仅当存在两种方案 \(c_{i,j}\ne c'_{i,j}\)

所以现在相当于计数每种 \(i\times j\) 矩阵 \(c\) 的可能情况,要求满足每行、每列的和 \(>0\)(表示 \(P\)\(P^-\) 中的上升段不为空)并且矩阵和 \(=n\)(显然 \(P\) 中所有上升段的大小和为 \(n\)),记作 \(t_{i,j}\)。至于这个双射的证明是容易的。

第一种限制较为严格不好满足,考虑计数 \(i\times j\) 且和 \(=n\) 的矩阵后容斥,记为 \(w_{i,j}\)

钦定有 \(x\)\(y\) 列和 \(>0\)

\[w_{i,j}=\sum_{x=0}^i\sum_{y=0}^j{i\choose x}{j\choose y}t_{x,y} \]

发现又是二项式反演的形式。

\[t_{i,j}=\sum_{x=0}^i\sum_{y=0}^j{i\choose x}{j\choose y}(-1)^{i+j-x-y}w_{x,y} \]

\(w_{i,j}\) 是容易得到的,\({n+i\cdot j-1\choose i\cdot j-1}\)

于是两个反演就能做到 \(O(n^4)\) 容斥。还需优化,以 \(t\) 为例:

\[t_{i,j}=\sum_{x=0}^i\sum_{y=0}^j{i\choose x}{j\choose y}(-1)^{i+j-x-y}w_{x,y} \\=\sum_{x=0}^i {i\choose x}(-1)^{i-x}\sum_{y=0}^j{j\choose y}(-1)^{j-y}w_{x,y} \]

后一个求和与 \(i\) 无关,于是套路的先计算 \(l_{x,y}=\sum_{y=0}^j{j\choose y}(-1)^{j-y}w_{x,y}\)

\[t_{i,j}=\sum_{x=0}^i {i\choose x}(-1)^{i-x} l_{x,y} \]

就能够优化至 \(O(n^3)\)\(f\) 同理。

还卡常🔥🔥🔥🔥🔥,容斥的时候 \(-1\) 不要写成 \(mod-1\) 的形式,最后输出答案的时候再考虑负数,不然 \(mod\) 的次数会增加很多。还有组合数可以预处理也能避免多次计算时的 \(mod\)

UesugiErii
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
using namespace __gnu_pbds;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
    ll x=0,f=1;char ch=nc();
    while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
    while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
   	return x*f;
}
//void write(int x){cout<<x<<' ';}
//void write(pii x){cout<<"P("<<x.fi<<','<<x.se<<")\n";}
//void write(vector<auto>x){for(auto i:x)write(i);cout<<'\n';}
//void write(auto *a,int l,int r){for(int i=l;i<=r;i++)write(a[i]);cout<<'\n';}
inline ll lowbit(ll x){return x&-x;}
#define pcount(x) __builtin_popcount(x)
inline void cmx(auto &x,ll y){if(y>x)x=y;}
inline void cmn(auto &x,ll y){if(y<x)x=y;}
inline int max(vector<int>w){int res=-1e9;for(int i:w)cmx(res,i);return res;}
int mod=998244353;
ll qp(ll x,int y){ll res=1;for(;y;x=x*x%mod,y>>=1)if(y&1)res=res*x%mod;return res;}
const int N=505;
ll g[N][N],c[N][N],n,f[N][N],ans[N][N],fac[N*N+N],ifac[N*N+N];
inline ll C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
inline ll calc(int x,int y){return C(n+x*y-1,x*y-1);}
inline void UesugiErii(){
	cin>>n>>mod;
	for(int i=fac[0]=1;i<=n*n+n;++i)fac[i]=fac[i-1]*i%mod;
	ifac[n*n+n]=qp(fac[n*n+n],mod-2);
	for(int i=n*n+n;i;--i)ifac[i-1]=ifac[i]*i%mod;
	for(int i=0;i<=n;++i)
		for(int j=c[i][0]=1;j<=i;++j){
			c[i][j]=c[i-1][j-1]+c[i-1][j];
			if(c[i][j]>=mod)c[i][j]-=mod;
		}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			for(int k=1;k<=j;++k)
				(g[i][j]+=c[j][k]*((j-k)&1?-1:1)*calc(i,k))%=mod;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			for(int k=1;k<=i;++k)
				(f[i][j]+=c[i][k]*((i-k)&1?-1:1)*g[k][j])%=mod;		
	for(int i=0;i<n;++i)
		for(int j=0;j<n;++j){
			g[i][j]=0;
			for(int k=j;k<n;++k)
				(g[i][j]+=c[k][j]*((k-j)&1?-1:1)*f[n-i][n-k])%=mod;
	}
	for(int i=0;i<n;++i)
		for(int j=0;j<n;++j)
			for(int k=i;k<n;++k)
				(ans[i][j]+=c[k][i]*((k-i)&1?-1:1)*g[k][j])%=mod;
	for(int i=0;i<n;i++,cout<<'\n')
		for(int j=0;j<n;j++)cout<<(ans[i][j]+mod)%mod<<' ';
}
signed main(){
	//IO();
	cfast;
	int _=1;//cin>>_;
	for(;_;_--)UesugiErii();
	return 0;
}
posted @ 2025-12-10 20:42  Uesugi1  阅读(6)  评论(0)    收藏  举报