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

正题

      想出来也写不出来。

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

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

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

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

#include
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 V;
vector 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]=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&now,int idft){
	for(int i=0;i=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>1]>>1)|((i&1)<<(l-1)));
	for(int i=a[x].size();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

 

你可能感兴趣的:(Graph,2020 年百度之星·程序设计大赛 - 初赛三,分治NTT)