6728. 【2020.06.16省选模拟】T2 战棋游戏

题目


正解

有个比较显然的思路是给特殊点划分到cc个集合中(集合可以为空)。
先解决一个子问题:一条链,两端相同和两端不同的答案分别是什么。
这个东西可以简单递推,然后用矩阵乘法优化。
题解中有个结论:两端相同:(c1)len+(1)len(c-1)^{len}+(-1)^{len};两端不同:(c1)len+(1)len+1(c1)(c-1)^{len}+(-1)^{len+1}(c-1)
不要问我为什么,我也不会证。

考虑一个集合中的每个点的贡献。钦定顺时针方向连边(计算链的贡献),每条边的贡献挂在始点处。于是对于一个点而言,如果它顺时针的下一个点和它同在一个集合内,那就有两端相同的贡献;如果不同在一个集合内,那就有两端不同的贡献。
贡献算完了,我们发现剩下的就是个子集卷积。子集卷积有个很经典的问题:如何保证或卷积的子集没有交?
一种方法是像昨天那题一样枚举划分。但是复杂度要乘上划分数,不划算(gmh77就是这么写了,TLE90分)。
介绍一种新的方法:
[0,2k)[0,2^k)的数组FF中,每个位置存一个多项式。FS=xSSF_S=x^{|S|}*S的贡献
这样在子集卷积的过程中,xx的指数也在相加。这样子集大小始终是小于等于xx的指数的。当子集大小等于xx的指数的时候,不就是保证了子集之间没有交吗?
于是我们搞出这样的FF,然后卷ccFF,就可以搞到我们要的东西。

这样处理之后,对FF做一遍FWTFWT。由于FWTFWT中没有多项式乘法,所以时间复杂度为O(k22k)O(k^2 2^k)
接下来对每个位置求它的cc次方。求cc次方可以先lnlnexpexp求。
这里kk那么小写MTTMTT肯定不划算。于是就有了O(k2)O(k^2)暴力求lnlnexpexp的方法:
考虑求expexp,设g(x)=ef(x)g(x)=e^{f(x)}
g(x)=ef(x)f(x)=g(x)f(x)g'(x)=e^{f(x)}f'(x)=g(x)f'(x)
可以推出ngn=i=1nifigning_n=\sum_{i=1}^nif_ig_{n-i}
发现gng_n是从g1..n1g_{1..n-1}推过来的,于是直接递推即可。
移项就可以得到lnln的递推式:nfn=gn=i=1n1ifigninf_n=g_n=\sum_{i=1}^{n-1}if_ig_{n-i}
2k2^k个多项式求乘方,时间复杂度O(k22k)O(k^2 2^k)

搞完之后取出xkx^k项,然后做IFWTIFWT即可。由于我们只需要全集(即2k12^k-1)的答案,所以可以直接O(2k)O(2^k)容斥得到。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define ll long long
#define K 20
#define mo 1000000007
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
ll inv[K+1];
ll n,c;
int k,m;
ll a[K],len[K];
int id[K],re[K];
bool cmpid(int x,int y){return a[x]<a[y];}
ll h[K][2];
struct Matrix{
	ll m[2][2];
};
void operator*=(Matrix &a,Matrix b){
	static Matrix c;
	for (int i=0;i<2;++i)
		for (int j=0;j<2;++j){
			ll sum=0;
			for (int k=0;k<2;++k)
				sum+=a.m[i][k]*b.m[k][j];
			c.m[i][j]=sum%mo;
		}
	memcpy(&a,&c,sizeof c);
}
void getpow(Matrix &x,ll y){
	static Matrix r;
	r={1,0,0,1};
	for (;y;y>>=1,x*=x)
		if (y&1)
			r*=x;
	memcpy(&x,&r,sizeof r);
}
void calc(ll n,ll f[]){
	static Matrix T;
	T={(c-2)%mo,1,(c-1)%mo,0};
	getpow(T,n);
	f[1]=1*T.m[1][1];//[0 1]*T
	f[0]=1*T.m[0][1];//[1 0]*T
}
bool e[K][K];
int cnt[1<<K];
int dp[1<<K];
int f[1<<K][K+1],g[1<<K];
void fwt_or(int f[][K+1],int n){
	for (int i=1;i<1<<n;i<<=1)
		for (int j=0;j<1<<n;j+=i<<1)
			for (int k=j;k<j+i;++k)
				for (int t=0;t<=n;++t)
					(f[k+i][t]+=f[k][t])%=mo;
}
void getexp(int f[],int n){
	static int g[K+1];
	g[0]=1;
	for (int i=1;i<=n;++i){
		ll sum=0;
		for (int j=1;j<=i;++j)
			(sum+=(ll)j*f[j]%mo*g[i-j])%=mo;
		sum=sum*inv[i]%mo;
		g[i]=sum;
	}
	memcpy(f,g,sizeof(int)*(n+1));
}
void getln(int g[],int n){
	static int f[K+1];
	f[0]=0;
	for (int i=1;i<=n;++i){
		ll sum=0;
		for (int j=1;j<i;++j)
			(sum+=(ll)j*f[j]%mo*g[i-j])%=mo;
		sum=((ll)i*g[i]%mo-sum+mo)%mo*inv[i]%mo;
		f[i]=sum;
	}
	memcpy(g,f,sizeof(int)*(n+1));
}
int getpow(int f[],int n,ll c){
	getln(f,n);
	c%=mo;
	for (int i=0;i<=n;++i)
		f[i]=(ll)f[i]*c%mo;
	getexp(f,n);
	return f[n];
}
int main(){
	freopen("chess.in","r",stdin);
	freopen("chess.out","w",stdout);
	scanf("%lld%d%d%lld",&n,&k,&m,&c);
	for (int i=0;i<k;++i){
		scanf("%lld",&a[i]);
		--a[i];
		id[i]=i;
	}
	inv[1]=1;
	for (int i=2;i<=k;++i)
		inv[i]=(mo-mo/i)*inv[mo%i]%mo;
	sort(id,id+k,cmpid);
	for (int i=0;i<k;++i)
		re[id[i]]=i;
	for (int i=1;i<=m;++i){
		int u,v;
		scanf("%d%d",&u,&v);
		--u,--v;
		u=re[u],v=re[v];
		e[u][v]=e[v][u]=1;
	}
	sort(a,a+k);
	for (int i=0;i<k;++i){
		int j=(i+1)%k;
		len[i]=(a[j]-a[i]+n)%n;
		if (len[i]==1)
			e[i][j]=e[j][i]=1;
		calc(len[i],h[i]);
	}
	dp[0]=1;
	for (int i=0;i<k;++i)
		for (int s=0;s<1<<i;++s)
			if (dp[s]){
				bool ok=1;
				for (int t=0;t<k && ok;++t)
					if (s>>t&1 && e[i][t])
						ok=0;
				if (ok==0)
					continue;
				dp[s|1<<i]=1;
			}
	for (int s=0;s<1<<k;++s){
		cnt[s]=cnt[s>>1]+(s&1);
		if (dp[s]){
			ll pro=1;
			for (int i=0;i<k;++i)
				if (s>>i&1){
					int j=(i+1)%k;
					(pro*=h[i][s>>j&1])%=mo;
				}
			f[s][cnt[s]]=pro;
		}
	}
	fwt_or(f,k);
	for (int s=0;s<1<<k;++s)
		g[s]=getpow(f[s],k,c);
	ll ans=0;
	for (int s=0;s<1<<k;++s)
		ans+=(k-cnt[s]&1?-1:1)*g[s];
	ans=(ans%mo+mo)%mo;
	printf("%lld\n",ans);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章