題目
正解
有個比較顯然的思路是給特殊點劃分到個集合中(集合可以爲空)。
先解決一個子問題:一條鏈,兩端相同和兩端不同的答案分別是什麼。
這個東西可以簡單遞推,然後用矩陣乘法優化。
題解中有個結論:兩端相同:;兩端不同:
不要問我爲什麼,我也不會證。
考慮一個集合中的每個點的貢獻。欽定順時針方向連邊(計算鏈的貢獻),每條邊的貢獻掛在始點處。於是對於一個點而言,如果它順時針的下一個點和它同在一個集合內,那就有兩端相同的貢獻;如果不同在一個集合內,那就有兩端不同的貢獻。
貢獻算完了,我們發現剩下的就是個子集卷積。子集卷積有個很經典的問題:如何保證或卷積的子集沒有交?
一種方法是像昨天那題一樣枚舉劃分。但是複雜度要乘上劃分數,不划算(gmh77就是這麼寫了,TLE90分)。
介紹一種新的方法:
將的數組中,每個位置存一個多項式。。
這樣在子集卷積的過程中,的指數也在相加。這樣子集大小始終是小於等於的指數的。當子集大小等於的指數的時候,不就是保證了子集之間沒有交嗎?
於是我們搞出這樣的,然後卷次,就可以搞到我們要的東西。
這樣處理之後,對做一遍。由於中沒有多項式乘法,所以時間複雜度爲。
接下來對每個位置求它的次方。求次方可以先再求。
這裏那麼小寫肯定不划算。於是就有了暴力求和的方法:
考慮求,設
則
可以推出
發現是從推過來的,於是直接遞推即可。
移項就可以得到的遞推式:
對個多項式求乘方,時間複雜度。
搞完之後取出項,然後做即可。由於我們只需要全集(即)的答案,所以可以直接容斥得到。
代碼
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;
}