Graph,2020 年百度之星·程序设计大赛 - 初赛三,分治NTT

正题

      想出来也写不出来。

      其实题解说上去还挺简单的,看上去写也是十分方便的,可是万万没想到出题人卡空间,对于一条树边,我原先的方案就是构造一个新的多项式"1,1",塞进里面,结果空间爆炸,换成组合数方法来乘起来,所用空间就还不到18MB。

       题解大概就是考虑这样的一个图一定是一个仙人掌,至于为什么,可以考虑一条边同时被两个环覆盖的情况,然后剩下的就很简单了,很容易就可以想到分治NTT,启发式合并还是不启发式合并完全看你心情,理论时间复杂度没有什么差别,都是两个log。

       然后做一个树上差分就可以判仙人掌和判一条边是否为树边。

       最后乘上往黑白格子填数的方案数就可以了。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int T,n,m;
struct edge{
	int y,next;
}s[N<<1];
int first[N],len,dep[N];
int t[N],op=0;
bool we=true;
int where[N<<2],l=0,limit;
int fac[N],inv[N]; 
const int mod=998244353;
vector<int> V;
vector<int> a[N];

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len; 
}

long long ksm(long long x,long long t){
	long long tot=1;
	while(t){
		if(t&1) (tot*=x)%=mod;
		(x*=x)%=mod;
		t/=2; 
	} 
	return tot;
}

void dfs(int x,int ex){
	for(int i=first[x];i!=0;i=s[i].next) if(i!=ex && i!=(ex^1)){
		if(dep[s[i].y] && dep[s[i].y]<dep[x]) V.push_back(dep[x]-dep[s[i].y]+1),t[x]++,t[s[i].y]--;
		else if(!dep[s[i].y]) dep[s[i].y]=dep[x]+1,dfs(s[i].y,i),t[x]+=t[s[i].y];
	}
	if(t[x]>=2) we=false;
	if(t[x]==0 && x!=1) op++;
}

int C(int x,int y){
	return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
}

void dft(vector<int>&now,int idft){
	for(int i=0;i<limit;i++) if(i<where[i]) swap(now[i],now[where[i]]);
	int a,b,w,wn;
	for(int l=2;l<=limit;l<<=1){
		wn=ksm(3,1ll*(mod-1)/l*idft%(mod-1));
		for(int i=0;i<limit;i+=l){
			w=1;
			for(int x=i,y=i+l/2;y<i+l;x++,y++,w=1ll*w*wn%mod){
				a=now[x],b=1ll*now[y]*w%mod;
				now[x]=a+b,now[x]>=mod?now[x]-=mod:0;
				now[y]=a+mod-b,now[y]>=mod?now[y]-=mod:0;
			}
		}
	}
}

void get_sum(int x,int y){
	int tot=a[x].size()-1+a[y].size();
	l=0;limit=1;while(limit<=a[x].size()-1+a[y].size()-1) limit*=2,l++;
	for(int i=0;i<limit;i++) where[i]=((where[i>>1]>>1)|((i&1)<<(l-1)));
	for(int i=a[x].size();i<limit;i++) a[x].push_back(0);
	for(int i=a[y].size();i<limit;i++) a[y].push_back(0);
	dft(a[x],1);dft(a[y],1);
	for(int i=0;i<limit;i++) a[x][i]=1ll*a[x][i]*a[y][i]%mod;
	dft(a[x],mod-2);
	long long tmp=ksm(limit,mod-2);
	for(int i=0;i<limit;i++) a[x][i]=1ll*a[x][i]*tmp%mod;
	a[x].resize(tot);a[y].resize(0);
}

void solve(int l,int r){
	if(l==r) return ;
	int mid=(l+r)/2;
	solve(l,mid);solve(mid+1,r);
	get_sum(l,mid+1);
}

int main(){
	scanf("%d",&T);
	fac[0]=1;for(int i=1;i<=100000;i++) fac[i]=1ll*fac[i-1]*i%mod;
	inv[100000]=ksm(fac[100000],mod-2);for(int i=99999;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
	while(T--){
		scanf("%d %d",&n,&m);op=0;
		memset(first,0,sizeof(first));len=1;
		memset(dep,0,sizeof(dep));V.resize(0);
		memset(t,0,sizeof(t));we=true;
		int x,y;
		for(int i=1;i<=m;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
		dep[1]=1;dfs(1,0);
		if(we==false){
			printf("0\n");
			continue;
		}
		for(int i=0;i<V.size();i++){
			a[i].resize(V[i]+1);
			for(int j=1;j<=V[i];j+=2) a[i][j]=C(V[i],j);
			for(int j=0;j<=V[i];j+=2) a[i][j]=0;
		}
		solve(0,V.size()-1);
		long long ans=0;
		for(int i=(m+1)/2-op;i<=(m+1)/2;i++) ans+=1ll*a[0][i]*C(op,(m+1)/2-i)%mod;ans%=mod;
		printf("%lld\n",1ll*ans*fac[(m+1)/2]%mod*fac[m/2]%mod);a[0].resize(0);
	}
} 

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/107598860