【Atcoder Grand Contest 043】【AGC043】C - Giant Graph(贪心)(SG函数)(FWT)

传送门


题解:

容易发现这个独立集直接从大到小贪心选择即可。

一个点能被选当且仅当所有比它大且与它相连的点都没有被选(容易发现 i + j + k i+j+k i+j+k 相同的点之间不会有边)。

发现这个和 I C G ICG ICG 的局面 N P NP NP 性是一样的,这里有三个独立游戏,分别算一下 S G SG SG 函数,最后 S G SG SG 值为 0 0 0 的局面都可以选。

可以 F W T FWT FWT ,也可以暴力计算,因为 m m m 条边的这种游戏, S G SG SG 值上界只有 O ( m ) O(\sqrt m) O(m )


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}template<typename T>T get_integer(){
		char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
		while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
	}inline int gi(){return get_integer<int>();}
}using namespace IO;

using std::cerr;
using std::cout;

cs int mod=998244353,Val=(ll)(1e18)%mod;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;} 
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline void ex_gcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return ;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}inline int inv(int a){int x,y;ex_gcd(mod,a,y,x);return x+(x>>31&mod);}

cs int SIZE=1<<10|7;

int S,ivS;

void FWT(int *A){
	for(int re i=1;i<S;i<<=1)
		for(int re j=0;j<S;j+=i<<1)
			for(int re k=0;k<i;++k){
				int x=A[j+k],y=A[i+j+k];
				A[j+k]=add(x,y);
				A[i+j+k]=dec(x,y);
			}
}

void IFWT(int *A){
	FWT(A);for(int re i=0;i<S;++i)Mul(A[i],ivS);
}

int sm[3][SIZE];

cs int N=1e5+7;

int n,m,mx,ans;

int el[N],nx[N],to[N],ec;
inline void adde(int u,int v){
	if(u>v)std::swap(u,v);
	nx[++ec]=el[u],el[u]=ec,to[ec]=v;
}

void clr(){
	memset(el,0,(n+1)<<2);ec=0;
}

int pw[N],sg[N],vs[SIZE];
void calc(int *sm){
	for(int re u=n;u;--u){
		for(int re e=el[u];e;e=nx[e])
			vs[sg[to[e]]]=true;
		sg[u]=0;while(vs[sg[u]])++sg[u];
		for(int re e=el[u];e;e=nx[e])
			vs[sg[to[e]]]=false;
		Inc(sm[sg[u]],pw[u]);
	}
}

void Main(){
	n=gi();
	for(int re i=pw[0]=1;i<=n;++i)
		pw[i]=mul(pw[i-1],Val);
	for(int re t=0;t<3;++t){
		m=gi();mx=std::max(mx,m);
		for(int re i=1;i<=m;++i)
			adde(gi(),gi());
		calc(sm[t]);clr();
	}for(S=1;S*S>>1<mx;S<<=1);ivS=inv(S);
	FWT(sm[0]),FWT(sm[1]);
	for(int re i=0;i<S;++i)
		Mul(sm[0][i],sm[1][i]);
	IFWT(sm[0]);
	for(int re i=0;i<S;++i)
		Inc(ans,mul(sm[0][i],sm[2][i]));
	cout<<ans<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("C.in","r",stdin);
#endif
}signed main(){file();Main();return 0;} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值