Comet OJ - Contest #11题解

传送门

\(A\)

咕咕咕

const int N=1e6+5;
char s[N],t[N];int n,res;
inline bool cmp(const int &x,const int &y){return x>y;}
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	fp(i,1,n)t[i]=s[i];sort(t+1,t+1+n,cmp);
	fp(i,1,n)res=(res*10+t[i]-s[i]),res=(res%10+10)%10;
	printf("%d\n",res);
	return 0;
}

\(B\)

先预处理出\(dp[i]\)表示花费\(i\)的代价最多能在\(n\)天之后获得的回报,然后直接\(dp\)就行了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=10005;
ll dp[N],f[2][N],res;int w[N],n,m,k,t;
inline int calc(R int x){return x<=k?w[x]:0;}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	fp(i,0,k)scanf("%d",&w[i]);
	memset(dp,0xef,sizeof(dp)),dp[0]=0;
	for(R int a,b;m;--m){
		scanf("%d%d",&a,&b);
		fp(i,a,2000)if(dp[i-a]>=0)cmax(dp[i],dp[i-a]+b);
	}
	memset(f[0],0xef,sizeof(f[0])),f[t=0][calc(0)]=0;
	fp(i,1,n-1){
		memset(f[t^1],0xef,sizeof(f[t^1]));
		fp(j,0,2000)if(f[t][j]>=0){
//			printf("%d %d %lld\n",i,j,f[t][j]);
			fp(l,0,j)
			cmax(f[t^1][j-l+calc(j-l)],f[t][j]+dp[l]);
		}
		t^=1;
	}
	res=-1e18;
	fp(i,0,2000)if(f[t][i]>=0)cmax(res,f[t][i]+i);
	printf("%lld\n",res);
	return 0;
}

\(C\)

我觉得应该只有我一个人是\(zz\)到写了个\(O(n\log n)\)的大常数做法结果居然跑过去了的人……

做法一

首先一段的长度显然不能超过\({(n-1)\over 2}\)。先破环成链,我们记\(las_i\)表示以\(i\)为结尾,最小的\(j\)满足\([j+1,i]\)是一个合法的区间,同理定义\(nxt_i\)表示最大的\(j\)满足\([i,j-1]\)是一个是一个合法的区间

我们枚举其中一个区间的末尾,那么最终得到的答案除以\(3\)就是原来的答案了

假设末尾为\(t\),那么这个环的开头就是\(s=t-n+1\),我们需要数的就是满足\(i\in[las_j,j-1],i\in [s,nxt_s-1],j\in [las_t,i-1]\)的数对\(i,j\)的个数

我们可以把每个\(j\)看做一个对于\([las_j,j-1]\)的区间加,用个指针保证只有\(j\in [las_t,i-1]\)的区间有贡献,然后区间查询\([s,nxt_s-1]\)就可以了。一开始用线段树似乎\(MLE\)了,后来改成树状数组结果卡了半天的常最后\(955ms\)过了此题……

做法二

不破环成链了,定义稍微改一改,记\(las_i\)表示以\(i-1\)为结尾的最小的\(j\)满足\([j,i]\)是一个合法的区间(只考虑\(j>i\)的那些,也就是说这个区间实际上是\([j,n]|[1,i-1]\)),以及\(nxt_i\)表示最大的\(j\)满足\([i,j]\)是个合法的区间

我们枚举\(a,b\),则需要满足的条件是\(b\in [a+1,nxt_a+1],c\in [b+1,nxt_b+1],c\in[las_a,n]\)

为了方便起见我们把所有\(nxt_b>n-1\)的都设为\(n-1\),可以看出不会影响答案

因为每一个区间的长度都小于等于\({(n-1)\over 2}\),所以\(b<las_a\)恒成立,那么\(c\)的合法的取值方案就是\(nxt_b+2-las_a\)

那么我们枚举\(a\),用双指针维护所有合法的\(b\)的贡献即可

具体细节可以参考代码

代码一

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
typedef long long ll;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=4e6+5;
ll d[N];int c[N];
int a[N],las[N],nxt[N],pos[N],n,m,len;ll res;
inline int max(R int x,R int y){return x>y?x:y;}
inline int min(R int x,R int y){return x<y?x:y;}
inline void chgd(R int x,R int v){for(;x<=m;x+=x&-x)d[x]+=v;}
inline void chgc(R int x,R int v){for(;x<=m;x+=x&-x)c[x]+=v;}
inline void upd(R int l,R int r,R int v){
	chgc(r+1,-v),chgd(r+1,-v*(r+1));
	if(l!=0)chgc(l,v),chgd(l,v*l);
}
inline int askc(R int x){R int res=0;for(;x;x-=x&-x)res+=c[x];return res;}
inline ll askd(R int x){R ll res=0;for(;x;x-=x&-x)res+=d[x];return res;}
inline ll query(R int l,R int r){
	R ll res=0;
	res+=1ll*(r+1)*askc(r)-askd(r);
	if(l>1)res-=1ll*l*askc(l-1)-askd(l-1);
	return res;
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read(),m=n<<1;
	fp(i,1,n)a[i]=read(),a[n+i]=a[i];
	len=(n-1)>>1;
	las[0]=0,nxt[n<<1|1]=(n<<1|1);
	fp(i,1,n<<1)las[i]=max(las[i-1],max(i-len,pos[a[i]])),pos[a[i]]=i;
	fp(i,1,n)pos[i]=(n<<1|1);
	fd(i,n<<1,1)nxt[i]=min(nxt[i+1],min(i+len,pos[a[i]])),pos[a[i]]=i;
//	fp(i,2,n)upd(las[i],i-1,1);
	fp(i,las[n+1],n)upd(las[i],i-1,1);
	for(R int i=n+1,j=las[n+1];i<=(n<<1);++i){
		while(j<las[i])upd(las[j],j-1,-1),++j;
		res+=query(i-n+1,nxt[i-n+1]-1);
		upd(las[i],i-1,1);
	}
	printf("%lld\n",res/3);
	return 0;
}

代码二

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
typedef long long ll;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=4e6+5;
int a[N],las[N],nxt[N],pos[N],c[N],n,m,len,cnt;ll res,sum;
inline int max(R int x,R int y){return x>y?x:y;}
inline int min(R int x,R int y){return x<y?x:y;}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read(),len=(n-1)>>1;
	fp(i,1,n)a[i]=read(),a[n+i]=a[i];
	las[0]=0,nxt[n<<1|1]=(n<<1|1);
	fp(i,1,n<<1)las[i]=max(las[i-1],max(i-len+1,pos[a[i]]+1)),pos[a[i]]=i;
	fp(i,1,n)pos[i]=(n<<1|1);
	fd(i,n<<1,1)nxt[i]=min(nxt[i+1],min(i+len-1,pos[a[i]]-1)),pos[a[i]]=i;
	fp(i,1,n)las[i]=las[i+n-1],cmin(nxt[i],n-1);
	/*
		fp(i,1,n)if(las[i]>i){
			fp(j,i+1,nxt[i]+1)if(nxt[j]+1>=las[i])res+=nxt[j]+2-las[i];
		}else break;
	*/
	for(R int i=1,j=1,l=1,r=1;i<=n;++i){
		if(las[i]<=i)break;
		while(r<=nxt[i]+1)++cnt,++c[nxt[r]+1],sum+=nxt[r]+2,++r;
		while(l<r&&(l<=i||nxt[l]+1<las[i]))--cnt,--c[nxt[l]+1],sum-=nxt[l]+2,++l;
		res+=sum-1ll*cnt*las[i];
	}
	printf("%lld\n",res);
	return 0;
}

\(D\)

类似于\(kruskal\)重构树的过程,我们从小到大枚举点,且只考虑那些从大连向小的边。对于新建出的这棵树,我们要保证每个点都小于其父亲节点,所以对于当前点枚举所有出边,把对应的连通块接到当前点的子树里就行了

那么这就是一个类似于\(kruskal\)重构树的东西了,询问的时候倍增\(+\)线段树就行了

//quming
#include<bits/stdc++.h>
#define R register
#define pb emplace_back
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=5e5+5;
vector<int>to[N];
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void Add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int a[N],rk[N],dfn[N],low[N],fa[N][35],ga[N],dep[N],Log[N],tim,n,m,q;
inline int find(R int x){return ga[x]==x?x:ga[x]=find(ga[x]);}
struct node;typedef node* ptr;
struct node{
	ptr lc,rc;int v;
	inline void upd(){v=mul(lc->v,rc->v);}
}ee[N<<2],*pp=ee,*rt;
void build(ptr &p,int l,int r){
	p=++pp;if(l==r)return p->v=a[rk[r]],void();
	int mid=(l+r)>>1;
	build(p->lc,l,mid),build(p->rc,mid+1,r);
	p->upd();
}
void update(ptr p,int l,int r,int x,int v){
	if(l==r)return p->v=v,void();
	int mid=(l+r)>>1;
	x<=mid?update(p->lc,l,mid,x,v):update(p->rc,mid+1,r,x,v);
	p->upd();
}
int query(ptr p,int l,int r,int ql,int qr){
	if(ql<=l&&qr>=r)return p->v;
	int mid=(l+r)>>1,res=1;
	if(ql<=mid)res=mul(res,query(p->lc,l,mid,ql,qr));
	if(qr>mid)res=mul(res,query(p->rc,mid+1,r,ql,qr));
	return res;
}
void dfs(int u,int f){
	dfn[u]=++tim,rk[tim]=u,fa[u][0]=f;
	fp(i,1,20)fa[u][i]=fa[fa[u][i-1]][i-1];
	go(u)dfs(v,u);
	low[u]=tim;
}
inline int jump(R int u,R int x){
	if(u>x)return 0;
//	R int k=dep[u];
	fd(i,20,0)if(fa[u][i]&&fa[u][i]<=x)u=fa[u][i];
	return query(rt,1,n,dfn[u],low[u]);
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d%d",&n,&m,&q);
	fp(i,1,n)scanf("%d",&a[i]),ga[i]=i,a[i]%=P;
	for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),to[u].pb(v),to[v].pb(u);
	fp(u,1,n)for(auto v:to[u])if(v<u&&u!=find(v))Add(u,find(v)),ga[find(v)]=u;
//	fp(i,1,n)fa[i][0]=ga[i];
	Log[0]=-1;fp(i,2,n)Log[i]=Log[i>>1]+1;
	dfs(n,0);
	build(rt,1,n);
	for(int op,u,v;q;--q){
		scanf("%d%d%d",&op,&u,&v);
		if(op==1)printf("%d\n",jump(u,v));
			else update(rt,1,n,dfn[u],v%P);
	}
	return 0;
}

\(E\)

首先,如果造成的总伤害为\(x\),那么这些伤害是等价的,所以分配给\(n\)个人的方案数等价于用\(n-1\)个隔板插进\(x-1\)个空当中的方案数,再乘上造成\(x\)点伤害的方案数就是对应的贡献

我们设\(F_i(x)\)的第\(k\)项表示对于第\(i\)个种族的一个生物,往里插\(k\)个隔板的总的贡献,那么答案就是\([x^{n-1}]\prod_{i=1}^m F_i^{a^i}(x)\)

然后考虑一个种族的生物的贡献是什么,分别设为\(a,b\),首先\([x^0]F_i(i)=b\),即不插隔板,则\(i\)选择造成的伤害总共有\(b\)种情况。如果把伤害看成一堆点,我们往中间插隔板,那么会发现两个生物连接起来的时候,我们还需要考虑连接处是否插隔板。那么对于插\(k>0\)个隔板的时候,我们默认最后一个点的后面那个位置也能插,所以贡献为

\[\begin{aligned} \sum_{i=1}^b {i\choose k}={b+1\choose k+1} \end{aligned} \]

这样的话记得把最后一个生物给特判一下

因为多项式长度不超过\(O(n)\),所以总复杂度为\(O(nm\log n)\),当然直接暴力多项式快速幂也挺快就是了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=(1<<18)+5,P=998244353;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
int r[21][N],rt[2][N<<1],inv[N],lg[N],lim,d;
void init(){
    fp(d,1,18){
        fp(i,1,(1<<d)-1)r[d][i]=(r[d][i>>1]>>1)|((i&1)<<(d-1));
        lg[1<<d]=d;
    }
    inv[0]=inv[1]=1;
    fp(i,2,262144)inv[i]=mul(P-P/i,inv[P%i]);
    for(R int t=(P-1)>>1,i=1,x,y;i<262144;i<<=1,t>>=1){
        x=ksm(3,t),y=ksm(332748118,t),rt[0][i]=rt[1][i]=1;
        fp(k,1,i-1)
            rt[1][i+k]=mul(rt[1][i+k-1],x),
            rt[0][i+k]=mul(rt[0][i+k-1],y);
    }
}
void NTT(int *A,int ty){
    fp(i,0,lim-1)if(i<r[d][i])swap(A[i],A[r[d][i]]);
    for(R int mid=1;mid<lim;mid<<=1)
        for(R int j=0,t;j<lim;j+=(mid<<1))
            fp(k,0,mid-1)
                A[j+k+mid]=dec(A[j+k],t=mul(rt[ty][mid+k],A[j+k+mid])),
                A[j+k]=add(A[j+k],t);
    if(!ty)fp(i,0,lim-1)A[i]=mul(A[i],inv[lim]);
}
void Mul(const int *a,const int *b,int *c,int len){
	static int A[N],B[N];
	lim=(len<<1),d=lg[lim];
	fp(i,0,len-1)A[i]=a[i],B[i]=b[i];
	fp(i,len,lim-1)A[i]=B[i]=0;
	NTT(A,1),NTT(B,1);
	fp(i,0,lim-1)A[i]=mul(A[i],B[i]);
	NTT(A,0);
	fp(i,0,len-1)c[i]=A[i];
	fp(i,len,lim-1)c[i]=0;
}
void Inv(int *a,int *b,int len){
    if(len==1)return b[0]=ksm(a[0],P-2),void();
    Inv(a,b,len>>1);
    static int A[N],B[N];lim=(len<<1),d=lg[lim];
    fp(i,0,len-1)A[i]=a[i],B[i]=b[i];
    fp(i,len,lim-1)A[i]=B[i]=0;
    NTT(A,1),NTT(B,1);
    fp(i,0,lim-1)A[i]=mul(A[i],mul(B[i],B[i]));
    NTT(A,0);
    fp(i,0,len-1)b[i]=dec(add(b[i],b[i]),A[i]);
    fp(i,len,lim-1)b[i]=0;
}
void Ln(int *a,int *b,int len){
    static int A[N],B[N];
    fp(i,1,len-1)A[i-1]=mul(a[i],i);A[len-1]=0;
    Inv(a,B,len);lim=(len<<1),d=lg[lim];
    fp(i,len,lim-1)A[i]=B[i]=0;
    NTT(A,1),NTT(B,1);
    fp(i,0,lim-1)A[i]=mul(A[i],B[i]);
    NTT(A,0);
    fp(i,1,len-1)b[i]=mul(A[i-1],inv[i]);b[0]=0;
    fp(i,len,lim-1)b[i]=0;
}
void Exp(int *a,int *b,int len){
    if(len==1)return b[0]=1,void();
    Exp(a,b,len>>1);
    static int A[N];Ln(b,A,len);
    lim=(len<<1),d=lg[lim];
    A[0]=dec(a[0]+1,A[0]);
    fp(i,1,len-1)A[i]=dec(a[i],A[i]);
    fp(i,len,lim-1)A[i]=b[i]=0;
    NTT(A,1),NTT(b,1);
    fp(i,0,lim-1)b[i]=mul(A[i],b[i]);
    NTT(b,0);
    fp(i,len,lim-1)b[i]=0;
}
//inline void ksm(int *a,int *b,int len,int k){
//	static int res[N],A[N];
//	res[0]=1;fp(i,1,len-1)res[i]=0;
//	fp(i,0,len-1)A[i]=a[i];
//	for(;k;k>>=1,Mul(A,A,A,len))if(k&1)Mul(res,A,res,len);
//	fp(i,0,len-1)b[i]=res[i];
//}
inline void ksm(int *a,int *b,int len,int k){
	static int A[N];R int t=a[0],it=ksm(t,P-2);
	t=ksm(t,k);
	fp(i,0,len-1)a[i]=mul(a[i],it);
	assert(a[0]==1);
	Ln(a,A,len);
	fp(i,0,len-1)A[i]=mul(A[i],k);
	Exp(A,b,len);
	fp(i,0,len-1)b[i]=mul(b[i],t);
}
int a[N],b[N],ans[N],f[N],g[N],n,m;
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&m),init();
	fp(i,1,m)scanf("%d%d",&a[i],&b[i]);
	++m,a[m]=1,b[m]=b[m-1]-1,--a[m-1];
	R int len=1;while(len<n)len<<=1;
	ans[0]=1;
	fp(i,1,m)if(a[i]){
		f[0]=b[i];R int t=b[i]+1;
		fp(k,1,len-1){
			t=mul(t,mul(inv[k+1],b[i]+1-k));
			f[k]=t;
		}
		if(i==m)++f[0];
		ksm(f,g,len,a[i]);
		Mul(ans,g,ans,len);
	}
	printf("%d\n",ans[n-1]);
	return 0;
}

\(F\)

显然并没有搞懂子集卷积的实质啊……

首先\(DAG\)计数的套路就是容斥,即对于集合\(S\),我们枚举它的哪一个子集\(T(T\neq \varnothing)\)出度为\(0\),然后乘个容斥系数就好了

\(es_S\)表示集合\(S\)中的边数之和,即总的方案数是

\[\begin{aligned} &f_S=\sum_{T\subseteq S,T\neq \varnothing}(-1)^{|T|-1}2^{es_S-es_{T}-es_{S-T}}f_{S-T}\\ &{f_S\over 2^{es_S}}=\sum_{T\subseteq S,T\neq \varnothing}{(-1)^{|T|-1}\over 2^{es_{T}}}{f_{S-T}\over 2^{es_{S-T}}}\\ \end{aligned} \]

发现这是一个子集卷积的形式,类似于集合并卷积的性质,我们发现如果把\(f,g\)都莫比乌斯变换之后系数等于对应项的点值之积,那么就可以\(O(n^22^n)\)求出\(f\)

具体细节可以看代码

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=998244353,inv=499122177;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=25,L=(1<<20)+5;
int sz[L],es[L],f[N][L],g[N][L],Lg[L],bin[N*N],n,m,lim;
inline void fwt(int *a,int len){
	for(R int i=1;i<len;i<<=1)
		for(R int p=(i<<1),j=0;j<len;j+=p)
			fp(k,0,i-1)upd(a[i+j+k],a[j+k]);
}
inline void ifwt(int *a,int len){
	for(R int i=1;i<len;i<<=1)
		for(R int p=(i<<1),j=0;j<len;j+=p)
			fp(k,0,i-1)upd(a[i+j+k],P-a[j+k]);
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&m),lim=(1<<n);
	fp(i,1,lim-1)sz[i]=sz[i>>1]+(i&1);
	fp(i,0,n-1)Lg[1<<i]=i;
	bin[0]=1;fp(i,1,n*n)bin[i]=mul(bin[i-1],inv);
	for(R int i=1,u,v;i<=m;++i){
		scanf("%d%d",&u,&v),--u,--v;
		fp(s,0,lim-1)if((s>>u&1)&&(s>>v&1))++es[s];
	}
	fp(i,0,lim-1)g[sz[i]][i]=sz[i]&1?bin[es[i]]:P-bin[es[i]];
	fp(i,0,n)fwt(g[i],lim);
	f[0][0]=1,fwt(f[0],lim);
	fp(i,1,n)fp(j,0,i-1)fp(k,0,lim-1)upd(f[i][k],mul(f[j][k],g[i-j][k]));
	ifwt(f[n],lim);
	printf("%d\n",mul(f[n][lim-1],ksm(mul(2,ksm(3,P-2)),m)));
	return 0;
}
posted @ 2019-09-23 22:10  源曲明  阅读(261)  评论(0编辑  收藏  举报