『数学』做题记录2

P6633 [ZJOI2020] 抽卡

P5401 [CTS2019] 珍珠

P5824 十二重计数法

UOJ597「2021 集训队互测」数圈圈

不知道会不会有人看不懂官方题解,我就自己做个总结啥的,大家就当看个乐子。

Part1 前置知识

LibreOJ 6073 小 Q 的序列

考虑 \(\sum\limits_{p}\prod\limits_{i=1}^{k} \left(a_{p_i}+i\right)\) 通过 dp 计算,设 \(f_{i,j}\) 表示前 \(i\) 个数,没有选在 \(p\) 中的有 \(j\) 个,转移:

\[f_{i,j}=(a_i+(i-j))f_{i-1,j}+f_{i-1,j-1} \]

写成生成函数形式,有 \([x^j]F_{i}(x)=f_{i,j}\)

\[F_{i}(x)=(a_i+i+x)F_{i-1}(x)-xF_{i-1}^{\prime}(x) \]

\[F_{i}(x)=(a_i+i)F_{i-1}(x)+x\left(F_{i-1}(x)-F_{i-1}^{\prime}(x)\right) \]

\(b_i=a_i+i\)

\[F_{i}(x)=b_iF_{i-1}(x)+x\left(F_{i-1}(x)-F_{i-1}^{\prime}(x)\right) \]

右边的这个式子丑陋,考虑用高中数学解决:

构造 \(G_i(x)=F_{i}(x)e^{-x}\),有:

\[G_{i}(x)=b_iG_{i-1}(x)+xG_{i-1}^{\prime}(x) \]

\[G_{n}(x)=\prod_{i=1}^{n}\left(b_i+\dfrac{d}{dx}x \right) \]

不难发现一个事实,就是对于后面的部分其实并不会将函数平移,而我们只关心 \([x^j]G_{n}(x)\) 的值,所以有:

\[[x^j]G_{n}(x)=[x^j]G_{0}(x)\prod_{i=1}^{n}\left(b_i+j \right) \]

这里已经有一种不是很优秀的做法,观察到 \(F_{0}(x)=1\),所以 \(G_{0}(x)=e^{-x}=1+\sum\limits_{i>0}\dfrac{(-1)^i}{i!}x^i\)

所以:

\[[x^j]G_{n}(x)=\dfrac{(-1)^j}{j!}\prod_{i=1}^{n}\left(b_i+j \right) \]

那么可以构造:

\[f(x)=\prod_{i=1}^{n}\left(b_i+x \right) \]

分治 fft 求出 \(f(x)\),然后多项式多点求值求出 \(f(0),f(1),\dots,f(n)\)

复杂度 \(O(n \log^2n)\),可以通过,注意常数。

Part2 Sub1~5

考虑枚举一个长度为 \(k\) 的子序列 \(\{ a_{p_1},a_{p_2},\dots,a_{p_k}\}\),考虑给每个边赋权值,所有选择边的权值积来表示最终该合法边子集的权值。

钦定只有这 \(k\) 个点有出边,且按照从小到大考虑每个点的出边,认为按照该顺序考虑这 \(k\) 个点的出边,若当前加入的边导致新增环,则该边权值为 \(y\),否则为 \(1\)

对于 \(i \in [1,k]\)\(p_i\)\(a_{p_i}-i+1\) 哥出边可以选择,考虑怎么知道什么时候会出现环,分讨一下:

  1. \(a_{p_i}<p_i\)

此时由于 \(a\) 单调不降,所以当前 \(p_i\) 是没有入度的,所有边的权值皆为 \(1\)

  1. \(a_{p_i}\geq p_i\)

此时必然存在存在一条链(可以是单独一个点 \(p_i\)),设链起点为 \(w\),因为我们从小到大考虑加边,所以有 \(w \leq p_i \leq a_{p_i}\),所以有且仅有一个 \(w\) 满足 \(p_i\) 连向其权值为 \(y\)

所以答案为:

\[\sum\limits_{p} \prod\limits_{i=1}^{k} \left(a_{p_i}-i+1+(y-1)\times[a_{p_i}\geq p_i] \right) \]

这个可以通过前缀知识做到 \(O(n \log^2 n)\)

Part3 Sub8

这个需要单独拎出来讲一下,与最后正解有极大关联。

这个限制很奇怪?但是稍微思考一下这个限制在做什么,由于 \(a_i\) 单调不升,考虑一个点 \(i\),可以到 \(i\) 的一定是一段前缀位置,\(i\) 可以到的点一定也是一段前缀,所以这个 \(a_i \geq b_i\) 意思就是可以到 \(i\) 的所有点一定可以被 \(i\) 到达。

现在考虑从大到小考虑每个点的出边,假设当前有一条从 \(s\rightarrow a\rightarrow a^{\prime}\rightarrow \dots\rightarrow p_i\) 的链,考虑是否存在一条从 \(i\)\(s\) 的边。

  1. \(a_{p_i}<p_i\)

此时由于 \(a\) 单调不升,所以当前 \(p_i\) 是没有入度的,所有边的权值皆为 \(1\)

  1. \(a_{p_i}\geq p_i\)

此时由于 \(s\rightarrow a\)\(a \geq p_i\) 是必然的,所以存在 \(s\rightarrow p_i\) 的边,又由于特殊性质,一定存在 \(p_i \rightarrow s\) 的边。

所以答案为:

\[\sum\limits_{p} \prod\limits_{i=1}^{k} \left(a_{p_i}+i-k+(y-1)\times[a_{p_i}\geq p_i] \right) \]

类似 part2 可以做到 \(O(n \log ^2 n)\)

Part4 Sub4~9

考虑没有这个特殊性质,你会发现无法确定是否存在可能成环的边。

首先之前相当于都是在图上找到一些链和环,那么大概猜测一下可以构造某个满足特殊性质的图,其任意环/链双射另一个图的同样点集的环/链。

具体构造考虑:

定义 \(E_i=\{(i,x)|x \geq i \} \cup \{(x,i)|x \geq i \}\)

如果 \(a_i\geq i \And a_i <b_i\),那么将所有 \(\geq i\) 的点连向 \(i\) 的边/ \(i\) 连向 \(\geq i\) 的点的边反向,即将 \(E_i\) 中所有边反向。

\(F(G)\) 表示图 \(G\) 的所有简单环和链的集合,设 \(G^r\) 表示上述构造得出的新图。

具体证明存在双射 \(\varphi(T),T\in F(G)\),满足:

\(\varphi(T)\) 的点集与 \(T\) 相等。

\(\varphi(T)\) 是简单环当且仅当 \(T\) 是简单环。

考虑归纳构造 \(\varphi\)

考虑依次\(n \rightarrow 1\)往图 \(G\) 中加入 \(E_i\),归纳加入 \([i+1,n]\) 的图 \(G^{\prime}\) 存在构造双射 \(\phi\),对于边界情况 \(i=n\),有 \(E=\varnothing\),此时构造 \(\varphi(x)=x\) 即可满足。

考虑如何构造 \(\varphi\)

对于不加入点 \(i\) 的情况,沿用之前的双射 \(\phi\),当前只讨论加入 \(i\) 的双射构造:

  1. 之前某条链 \(R\),在其末尾加入 \(i\)\(E_i\) 在新图上没有反向

构造 \(\varphi(R+i)=\phi(R)+i\),设 \(\phi(R)\) 末尾是 \(w\),若 \((w,i) \not\in G^r\),及 \((w,i) \not\in G\)\(w\) 在图 \(G\) 上没有出度,所以可得 \(w\)\(R\) 的末尾,而末尾与 \(i\)\(R+i\) 上相连,所以不符合\(w\) 在图 \(G\) 上没有出度,所以 \((w,i) \in G^r\)

  1. 之前某条链 \(R\),在其末尾加入 \(i\)\(E_i\) 在新图上有反向

构造 \(\varphi(R+i)=i+\phi(R)\),设 \(\phi(R)\) 开头是 \(w\),若 \((i,w) \not\in G^r\),及 \((w,i) \not\in G\)\(w\) 在图 \(G\) 上没有出度,所以可得 \(w\)\(R\) 的末尾,而末尾与 \(i\)\(R+i\) 上相连,所以不符合\(w\) 在图 \(G\) 上没有出度,所以 \((i,w) \in G^r\)

  1. 之前某条链 \(R\),在其开头加入 \(i\)\(E_i\) 在新图上没有反向

构造 \(\varphi(i+R)=i+\phi(R)\),设 \(\phi(R)\) 开头是 \(w\),若 \((i,w) \not\in G^r\),及 \((i,w) \not\in G\)\(w\) 在图 \(G\) 上没有入度,所以可得 \(w\)\(R\) 的开头,而开头与 \(i\)\(i+R\) 上相连,所以不符合 \(w\) 在图 \(G\) 上没有入度,所以 \((i,w) \in G^r\)

  1. 之前某条链 \(R\),在其开头加入 \(i\)\(E_i\) 在新图上有反向

构造 \(\varphi(i+R)=\phi(R)+i\),设 \(\phi(R)\) 末尾是 \(w\),若 \((w,i) \not\in G^r\),及 \((w,i) \not\in G\)\(w\) 在图 \(G\) 上没有入度,所以可得 \(w\)\(R\) 的开头,而开头与 \(i\)\(i+R\) 上相连,所以不符合\(w\) 在图 \(G\) 上没有入度,所以 \((w,i) \in G^r\)

  1. 之前某条链 \(R\),在其末尾开头加入 \(i\),即成环

构造 \(\varphi(i+R+i)=i+\phi(R)+i\),此处同理上面证明即可得出构造合法性。

  1. 之前某两条链 \(R_1,R_2\),在两链中间加入 \(i\)\(E_i\) 在新图上没有反向

构造 \(\varphi(R_1+i+R_2)=\phi(R_1)+i+\phi(R_2)\),此处同理上面证明即可得出构造合法性。

  1. 之前某两条链 \(R_1,R_2\),在两链中间加入 \(i\)\(E_i\) 在新图上没有反向

构造 \(\varphi(R_1+i+R_2)=\phi(R_2)+i+\phi(R_1)\),此处同理上面证明即可得出构造合法性。

至此我们已经构造出 \(\varphi\) 即转化的图 \(G^r\) 可以代替 \(G\) 计算贡献,沿用 part3 的做法即可做到 \(O(n \log^2 n)\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=4e5+20,M=1e6+20;
const int gen=3,invg=332748118,mod=998244353;
const int inv2=(mod+1)/2;
int qpow(int a,int b){
	int res=1;
	while(b){
		if(b&1) res=1ll*res*a%mod;
		a=1ll*a*a%mod;b>>=1;
	}return res;
}
struct Faction{
	int len;vector<int> v;
	inline void init(int l){len=l;v.resize(l+1);}
	inline int val(int x){
		int ans=0;
		for(int i=0,res=1;i<=len;i++,res=1ll*res*x%mod){
			ans=(ans+1ll*res*v[i]%mod)%mod;
		}return ans;
	}
};
int fac[N],ifac[N],inv[N];
namespace Poly{
	int tr[N],W[N];
	inline int init(int n){
		int lim=1,len=0;
		while(lim<=n) lim<<=1,len++;
		for(int i=1;i<lim;i++) tr[i]=(tr[i>>1]>>1)|((i&1)<<(len-1));
		return lim;
	}
	void NTT(Faction &f,bool opt){
		int len=f.len+1;
		for(int i=1;i<len;i++) if(i<tr[i]) swap(f.v[i],f.v[tr[i]]);
		for(int mid=1;mid<len;mid<<=1){
			int wn=qpow(opt?gen:invg,(mod-1)/(mid<<1));
			W[0]=1;for(int i=1;i<mid;i++) W[i]=1ll*W[i-1]*wn%mod;
			for(int j=0;j<len;j+=(mid<<1)) for(int k=0;k<mid;k++){
				int x=f.v[j+k],y=1ll*W[k]*f.v[j+k+mid]%mod;
				f.v[j+k]=(x+y)%mod;f.v[j+k+mid]=(x-y+mod)%mod; 
			}
		}
		if(!opt){
			int in=qpow(len,mod-2);
			for(int i=0;i<len;i++) f.v[i]=1ll*f.v[i]*in%mod;
		}
	}
	void print(Faction x){
		for(int i=0;i<=x.len;i++) cout<<x.v[i]<<' ';cout<<'\n';
	}
	inline Faction operator +(Faction a,Faction b){
		Faction c;int len=max(a.len,b.len);c.init(len);a.init(len);b.init(len);
		for(int i=0;i<=len;i++) c.v[i]=(a.v[i]+b.v[i])%mod;
		return c;
	}
	inline Faction operator -(Faction a,Faction b){
		Faction c;int len=max(a.len,b.len);c.init(len);a.init(len);b.init(len);
		for(int i=0;i<=len;i++) c.v[i]=(a.v[i]-b.v[i]+mod)%mod;
		return c;
	}
	inline Faction operator *(Faction a,Faction b){
		Faction c;
		int len=a.len+b.len,lim=init(len);
		a.init(lim-1);b.init(lim-1);c.init(lim-1);
		NTT(a,1);NTT(b,1);
		for(int i=0;i<lim;i++) c.v[i]=1ll*a.v[i]*b.v[i]%mod;
		NTT(c,0);c.init(len);
		return c;
	}
	inline Faction Mul(Faction a,Faction b){
		Faction c;
		int len=b.len,lim=init(len);
		a.init(lim-1);b.init(lim-1);c.init(lim-1);
		NTT(a,1);NTT(b,1);
		for(int i=0;i<lim;i++) c.v[i]=1ll*a.v[i]*b.v[i]%mod;
		NTT(c,0);c.init(len);
		return c;
	}
	inline Faction operator >>(Faction &f,int k){
		int len=f.len;f.init(len+k);
		for(int i=len;~i;i--) f.v[i+k]=f.v[i];
		for(int i=0;i<k;i++) f.v[i]=0;
		return f;
	}
	inline Faction rev(Faction &f){
		reverse(f.v.begin(),f.v.end());
		return f;
	}
	void getinv(Faction &f,Faction &g,int len){
		if(len==1) return f.init(0),f.v[0]=qpow(g.v[0],mod-2),void();
		getinv(f,g,(len+1)>>1);
		int lim=init((len-1)<<1);Faction h;h.init(lim-1);f.init(lim-1);
		for(int i=0;i<len;i++) h.v[i]=g.v[i];
		for(int i=len;i<lim;i++) h.v[i]=0;
		NTT(h,1);NTT(f,1);
		for(int i=0;i<lim;i++) f.v[i]=((2ll-1ll*f.v[i]*h.v[i]%mod)+mod)%mod*f.v[i]%mod;
		NTT(f,0);
		for(int i=len;i<lim;i++) f.v[i]=0;
	}
	Faction Inv(Faction &g){
		Faction f;
		getinv(f,g,g.len+1);
		f.init(g.len);return f;
	}
	inline Faction getdiv(Faction f,Faction g){
		Faction Q;
		int n=f.len,m=g.len;
		if(m>n) return Q.init(0),Q.v[0]=0,Q;
		rev(f);rev(g);g.init(n-m);g=Inv(g);
		Q=f*g;Q.init(n-m);rev(Q);
		return Q;
	}
	inline Faction getmod(Faction f,Faction g){
		Faction Q,R,F,G;F=f,G=g;
		int n=f.len,m=g.len;
		if(m>n) return f;
		rev(f);rev(g);g.init(n-m);g=Inv(g);
		Q=f*g;Q.init(n-m);rev(Q);
		Faction H=Q*G;R=F-H;R.init(m-1);
		return R;
	}
	inline Faction cdq_fft(int l,int r,int *x){
		if(l==r){
			Faction g;
			g.init(1);g.v[0]=x[l],g.v[1]=1;
			return g;
		}
		int mid=(l+r)>>1;
		return cdq_fft(l,mid,x)*cdq_fft(mid+1,r,x);
	}
	inline Faction operator /(Faction &f,Faction &g){return getdiv(f,g);}
	inline Faction operator %(Faction &f,Faction &g){return getmod(f,g);}
	Faction g[N<<2];
	inline void build(int rt,int l,int r,int *x){
		if(l==r){
			g[rt].init(1);g[rt].v[0]=(mod-x[l])%mod,g[rt].v[1]=1;
			return ;
		}
		int mid=(l+r)>>1;
		build(rt<<1,l,mid,x);build(rt<<1|1,mid+1,r,x);
		g[rt]=g[rt<<1]*g[rt<<1|1];
	}
	inline void Eva(int *x,int *y,int rt,int l,int r,Faction f){
		if(r-l<=32){
			for(int i=l;i<=r;i++) y[i]=f.val(x[i]);
			return ;
		}
		if(l==r) return y[l]=f.v[0],void();
		int mid=(l+r)>>1;
		Faction R=f%g[rt<<1];
		Eva(x,y,rt<<1,l,mid,R);
		R=f%g[rt<<1|1];
		Eva(x,y,rt<<1|1,mid+1,r,R);
	}
}
using namespace Poly;
inline int iep(int x){return x&1?mod-1:1;}
int x[N],y[N],a[N],b[N],s[N];
bool flag;
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	fac[0]=ifac[0]=ifac[1]=1;for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%mod;
	ifac[N-1]=qpow(fac[N-1],mod-2);for(int i=N-1;i;i--) ifac[i-1]=1ll*ifac[i]*i%mod;
	inv[0]=1;for(int i=1;i<N;i++) inv[i]=1ll*ifac[i]*fac[i-1]%mod;
	int n,Y;cin>>n>>Y;flag=1;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=2;i<=n;i++) flag&=(a[i]>=a[i-1]);
	if(flag){
		for(int i=1;i<=n;i++) b[i]=(a[i]+1+(Y-1)*(a[i]>=i)-i+mod)%mod;
		for(int i=0;i<=n;i++) x[i]=i;
		Faction f=cdq_fft(1,n,b),F,G,H;G.init(n);H.init(n);
		build(1,0,n,x);
		Eva(x,y,1,0,n,f);
		for(int i=0;i<=n;i++) G.v[i]=1ll*y[i]*ifac[i]%mod,H.v[i]=1ll*iep(i)*ifac[i]%mod;
		F=G*H;F.init(n);
		for(int i=n;~i;i--) cout<<F.v[i]<<' ';
	}else{
		if(!a[1]){for(int i=0;i<=n;i++) cout<<(!i)<<' ';return 0;}
		for(int i=1,p=n;a[i]>=i;i++){
			while(p>1&&a[p]<i) p--;
			int u=a[i]-i+1,v=p-i+1;if(u<v) swap(u,v);
			s[i]+=u,s[i+1]-=u,s[i+1]++,s[i+v]--;
		}
		for(int i=1;i<=n;i++) a[i]=s[i]+a[i-1];
		for(int i=1;i<=n;i++) a[i]=(a[i]+(Y-1)*(a[i]>=i))%mod;
		reverse(a+1,a+n+1);
		for(int i=1;i<=n;i++) b[i]=(a[i]+1-i+mod)%mod;
		for(int i=0;i<=n;i++) x[i]=i;
		Faction f=cdq_fft(1,n,b),F,G,H;G.init(n);H.init(n);
		build(1,0,n,x);
		Eva(x,y,1,0,n,f);
		for(int i=0;i<=n;i++) G.v[i]=1ll*y[i]*ifac[i]%mod,H.v[i]=1ll*iep(i)*ifac[i]%mod;
		F=G*H;F.init(n);
		for(int i=n;~i;i--) cout<<F.v[i]<<' ';
	}
	return 0;
}
/*
4 4
3 3 3 2
9 1
9 4 4 3 3 3 3 3 3
*/

posted @ 2023-12-13 20:32  Detect-Perplexity  阅读(26)  评论(0)    收藏  举报