题目
正解
有个比较显然的思路是给特殊点划分到个集合中(集合可以为空)。
先解决一个子问题:一条链,两端相同和两端不同的答案分别是什么。
这个东西可以简单递推,然后用矩阵乘法优化。
题解中有个结论:两端相同:;两端不同:
不要问我为什么,我也不会证。
考虑一个集合中的每个点的贡献。钦定顺时针方向连边(计算链的贡献),每条边的贡献挂在始点处。于是对于一个点而言,如果它顺时针的下一个点和它同在一个集合内,那就有两端相同的贡献;如果不同在一个集合内,那就有两端不同的贡献。
贡献算完了,我们发现剩下的就是个子集卷积。子集卷积有个很经典的问题:如何保证或卷积的子集没有交?
一种方法是像昨天那题一样枚举划分。但是复杂度要乘上划分数,不划算(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;
}