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;
}

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