LOJ#3160. 「NOI2019」斗主地(打表+组合数学+拉格朗日插值)

传送门
然而我这个并不是官方解法
网络赛的时候只会 30 30 30分的 O ( n 2 m ) d p O(n^2m)dp O(n2m)dp O ( n 3 l o g m ) O(n^3log_m) O(n3logm)的矩乘快速幂。
40 p t s 40pts 40pts代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int mod=998244353;
typedef long long ll;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=1e7+5,M=105;
int inv[N],n,a[N],f[N],coe[M][M],m,type;
inline void init(const int&up){
	inv[1]=1;
	for(ri i=2;i<=up;++i)inv[i]=mul(inv[mod-mod/i*i],mod-mod/i);
}
int ttt;
namespace subtask1{
	int tl[M],tr[M];
	inline void prepare(int n1,int n2){
		int p=1;
		for(ri i=1;i<=n1;++i)tl[i]=f[p],f[p++]=0;
		for(ri i=1;i<=n2;++i)tr[i]=f[p],f[p++]=0;
		memset(coe,0,sizeof(coe));
		for(ri i=n1;~i;--i)for(ri j=n2;~j;--j){
			if(!(i+j))continue;
			coe[i][j]=i+j==n1+n2?1:mul(inv[i+j+1],add(mul(i^n1?i+1:0,coe[i+1][j]),mul(j^n2?j+1:0,coe[i][j+1])));
			Add(f[i+j],mul(mul(inv[i+j],coe[i][j]),add(mul(i,tl[i]),mul(j,tr[j]))));
		}
	}
	inline void Main(){
		for(ri i=1;i<=m;++i)a[i]=read(),prepare(a[i],n-a[i]);
		for(ri tt=read();tt;--tt)cout<<f[read()]<<'\n';
		exit(0);
	}
}
struct Mat{
	int a[105][105];
	Mat(){memset(a,0,sizeof(a));}
	friend inline Mat operator*(const Mat&a,const Mat&b){
		Mat ret;
		for(ri i=1;i<=n;++i)for(ri k=1;k<=n;++k)if(a.a[i][k])
		for(ri j=1;j<=n;++j)if(b.a[k][j])Add(ret.a[i][j],mul(a.a[i][k],b.a[k][j]));
		return ret;
	}
	friend inline Mat operator^(Mat a,int p){
		Mat ret=a;
		for(--p;p;p>>=1,a=a*a)if(p&1)ret=ret*a;
		return ret;
	}
};
namespace subtask2{
	inline void Main(){
		for(ri i=1;i<=m;++i)a[i]=read();
		int n1=a[1],n2=n-a[1];
		Mat a,b;
		for(ri i=1;i<=n;++i)b.a[i][1]=f[i];
		for(ri i=n1;~i;--i)for(ri j=n2;~j;--j){
			if(!(i+j))continue;
			coe[i][j]=i+j==n1+n2?1:mul(inv[i+j+1],add(mul(i^n1?i+1:0,coe[i+1][j]),mul(j^n2?j+1:0,coe[i][j+1])));
			if(i)Add(a.a[i+j][i],mul(coe[i][j],mul(inv[i+j],i)));
			if(j)Add(a.a[i+j][n1+j],mul(coe[i][j],mul(inv[i+j],j)));
		}
		a=(a^m)*b;
		for(ri tt=read();tt;--tt)cout<<a.a[read()][1]<<'\n';
		exit(0);
	}
}
inline bool check1(){return n<=100&&m<=80;}
inline bool check2(){return n<=100&&m<=500000;}
int main(){
	n=read(),m=read(),type=read();
	init(n);
	for(ri i=1;i<=n;++i)f[i]=mul(i,type==1?1:i);
	if(check1())subtask1::Main();
	if(check2())subtask2::Main();
	return 0;
}

关于打表做法

首先要写一个 40 p t s 40pts 40pts的,然后观察 f i = i f_i=i fi=i的时候最后得到的 f i f_i fi是一个等差数列(大雾不知道是怎么想到打差分数列的表的
然后类推并且打表会发现 f i = i f_i=i fi=i的时候 f i f_i fi是一个二阶等差数列。
于是我们每次只用处理出 f 1 , 2 , 3 f_{1,2,3} f1,2,3就可以用拉格朗日插值求出整个 f f f,然后就能递推出下一轮的 f 1 , 2 , 3 f_{1,2,3} f1,2,3
考虑现在假设把整个数列分成 n 1 = A , n 2 = n − A n_1=A,n_2=n-A n1=A,n2=nA两份,如何算出最后第一份剩下 i i i个,第二份剩下 j j j个的概率(这个概率就是我 40 p t s 40pts 40pts代码中的 c o e i , j coe_{i,j} coei,j)

发现可以用组合计数。
我们把概率按分子分母分开考虑。
分母不管你怎么走最后都是 ∏ k = i + j + 1 n k \prod_{k=i+j+1}^nk k=i+j+1nk
分子不过你怎么走最后都是 ( ∏ k = i + 1 n 1 k ) ( ∏ k = j + 1 n 2 k ) (\prod_{k=i+1}^{n_1}k)(\prod_{k=j+1}^{n_2}k) (k=i+1n1k)(k=j+1n2k)
而一共有 C n − i − j n 1 − i C_{n-i-j}^{n1-i} Cnijn1i种走法。
所以概率就是 C n − i − j n 1 − i ( ∏ k = i + 1 n 1 k ) ( ∏ k = j + 1 n 2 k ) ∏ k = i + j + 1 n k C_{n-i-j}^{n1-i}\frac{(\prod_{k=i+1}^{n_1}k)(\prod_{k=j+1}^{n_2}k)}{\prod_{k=i+j+1}^nk} Cnijn1ik=i+j+1nk(k=i+1n1k)(k=j+1n2k)
然后就没啦。

100 p t s 100pts 100pts代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int mod=998244353;
typedef long long ll;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=1e7+5;
int up=3,fac[N],ifac[N],inv[N],f[4],s[4],t[4],n,m,type;
inline void init(){
	fac[0]=fac[1]=ifac[0]=ifac[1]=1,inv[1]=1;
	for(ri i=2;i<=n;++i)fac[i]=mul(fac[i-1],i),inv[i]=mul(inv[mod-mod/i*i],mod-mod/i);
	for(ri i=2;i<=n;++i)ifac[i]=mul(ifac[i-1],inv[i]);
}
inline int C(int n,int m){return (ll)fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
inline int lagrange(int*y,int x){
	if(x<=up)return y[x];
	static int pre[5],suf[5],ret;
	ret=0;
	pre[0]=suf[up+1]=1;
	for(ri i=1;i<=up;++i)pre[i]=mul(pre[i-1],x-i);
	for(ri i=up;i;--i)suf[i]=mul(suf[i+1],x-i);
	for(ri i=1,t;i<=up;++i){
		t=mul(mul(mul(pre[i-1],suf[i+1]),mul(ifac[i-1],ifac[up-i])),y[i]);
		(up-i)&1?Dec(ret,t):Add(ret,t);
	}
	return ret;
}
inline int coe(int n1,int n2,int i,int j){
	if(n1<i||n2<j)return 0;
	int t=mul(mul(ifac[n],fac[i+j]),C(n-i-j,n1-i));
	Mul(t,mul(fac[n1],ifac[i])),Mul(t,mul(fac[n2],ifac[j]));
	return t;
}
int main(){
	freopen("landlords.in","r",stdin);
    freopen("landlords.out","w",stdout);
	n=read(),m=read(),type=read()-1;
	init();
	for(ri i=1;i<=3;++i)f[i]=type?i*i:i;
	for(ri tt=1,x;tt<=m;++tt){
		x=read();
		swap(f,s);
		for(ri i=1;i<=3;++i)t[i]=lagrange(s,x+i),f[i]=0;
		for(ri ff,i=0;i<=3;++i)for(ri j=0;i+j<=3;++j){
			if(!(i+j))continue;
			ff=coe(x,n-x,i,j);
			Add(f[i+j],mul(mul(ff,inv[i+j]),add(mul(i,s[i]),mul(j,t[j]))));
		}
	}
	for(ri tt=read();tt;--tt)cout<<lagrange(f,read())<<'\n';
	return 0;
}

你可能感兴趣的:(#,拉格朗日插值,#,组合数学)