AGC 043 C,D 题解

Posted Gary2005

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AGC 043 C,D 题解相关的知识,希望对你有一定的参考价值。

AGC 043 C,D 题解

C - Giant Graph

首先可以理解成一个\\(N\\times N\\times N\\)的立方体从\\((N,N,N)\\)开始按照\\(x+y+z\\)降序贪心添加。

一个点不被选当且仅当按照\\(x,y,z\\)某一个方向可以到达一个选择的。

直接用\\(SG\\)函数即可。

由于\\(SG\\)的值域是\\(O(\\sqrt N)\\)的,所以可以暴力卷积,也可以用\\(O(\\sqrt N\\log\\sqrt N)\\)的FWT。

时间复杂度\\(O(N)\\)

/*
{
######################
#       Author       #
#        Gary        #
#        2021        #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<\'0\'||ch>\'9\'){
//        ch=getchar();
//    }
//    while(ch>=\'0\'&&ch<=\'9\'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/

const int MAXN=1e5+233;
int n;
int sg1[MAXN<<1],sg2[MAXN<<1],sg3[MAXN<<1];
int a1[MAXN<<1],a2[MAXN<<1],a3[MAXN<<1];
const int MOD=998244353;
int tmp[1<<17];
const int iv2=(MOD+1)/2;
void FMT(int * a,int x){
	for(int k=1;k<n;k<<=1){
		for(int j=0;j<n;j+=(k<<1)){
			rep(i,k){
				if(x==1) (tmp[i+j]=(a[i+j]+a[i+j+k])%MOD,tmp[i+j+k]=(MOD+a[i+j]-a[i+j+k])%MOD);
				else (tmp[i+j]=1ll*(a[i+j]+a[i+j+k])*iv2%MOD,tmp[i+j+k]=1ll*(MOD+a[i+j]-a[i+j+k])*iv2%MOD);
			}
		}
		rep(i,n) a[i]=tmp[i];
	}
}
vector<int> g[MAXN];
namespace GRAPH{
	void read(){
		int m;
		scanf("%d",&m);
		rb(i,1,m){
			int u,v;
			scanf("%d%d",&u,&v);
			if(v<u) swap(u,v);
			g[u].PB(v);
		}
	}
	int Mex(vector<int> v){
		sort(ALL(v));
		v.erase(unique(ALL(v)),v.end());
		v.PB(1000000000);
		rep(i,v.size()) if(v[i]!=i) return i;
		return 0;
	}
	vector<int> gao(){
		vector<int> sg(n+1,0);
		rl(i,n,1){
			vector<int> v;
			for(auto it:g[i]) v.PB(sg[it]);
			sg[i]=Mex(v);
		}
		return sg;
	}
	void clear(){
		rb(i,1,n) g[i].clear();
	}
}
int main(){
	scanf("%d",&n);
	GRAPH::read();
	vector<int> tmp=GRAPH::gao();
	rb(i,1,n) sg1[i]=tmp[i];
	GRAPH::clear();
	
	GRAPH::read();
	tmp=GRAPH::gao();
	rb(i,1,n) sg2[i]=tmp[i];
	GRAPH::clear();
	
	GRAPH::read();
	tmp=GRAPH::gao();
	rb(i,1,n) sg3[i]=tmp[i];
	GRAPH::clear();
	
	int ttmp=1;
	while(ttmp<n) ttmp<<=1;
	int _1018=1;
	rb(i,1,18) _1018=10ll*_1018%MOD;
	int T=1;
	rb(i,1,n) T=1ll*T*_1018%MOD,(a1[sg1[i]]+=T)%=MOD;
	T=1;
	rb(i,1,n) T=1ll*T*_1018%MOD,(a2[sg2[i]]+=T)%=MOD;
	T=1;
	rb(i,1,n) T=1ll*T*_1018%MOD,(a3[sg3[i]]+=T)%=MOD;
	n=ttmp;
	FMT(a1,1);
	FMT(a2,1);
	FMT(a3,1);
	rep(i,n) a1[i]=1ll*a1[i]*a2[i]%MOD*a3[i]%MOD;
	FMT(a1,-1);
	printf("%d\\n",a1[0]);
	return 0;
}

D - Merge Triplets

如果一个元素不是前缀最大值则一定和前一个元素属于同一堆。

这样就有长度为\\(1,2,3\\) 的段。

然后将\\(1,2\\)两两匹配。

剩下只需要有\\(3\\)的倍数个\\(1\\)即可。

\\(dp[i][j][k]\\)表示考虑了前\\(i\\)个,有\\(j\\)个1段,\\(k\\) 个2 段的方案数。

可以发现\\(j,k\\)之中必有一个\\(0\\)

所以状态时\\(O(N^2)\\)的,转移直接枚举下一段长度即可。

/*
{
######################
#       Author       #
#        Gary        #
#        2021        #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<\'0\'||ch>\'9\'){
//        ch=getchar();
//    }
//    while(ch>=\'0\'&&ch<=\'9\'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=6002;
int MOD,n,dp[MAXN][MAXN<<1];
int T(int i,int k){
	if(k==1) return 1;
	if(k==2) return i+1;
	return 1ll*(i+1)*(i+2)%MOD;
}
int main(){
	scanf("%d%d",&n,&MOD);
	n*=3;
	dp[0][MAXN]=1;
	rb(i,0,n-1){
		rb(j,-n,n)
			if(dp[i][j+MAXN])
				rb(k,1,min(3,n-i)){
					if(k==3){
						(dp[i+3][j+MAXN]+=1ll*dp[i][j+MAXN]*T(i,k)%MOD)%=MOD;
					}
					if(k==2){
						(dp[i+2][j-1+MAXN]+=1ll*dp[i][j+MAXN]*T(i,k)%MOD)%=MOD;
					}
					if(k==1){
						(dp[i+1][j+1+MAXN]+=1ll*dp[i][j+MAXN]*T(i,k)%MOD)%=MOD;
					}
				}
	}
	int rest=0;
	rb(i,0,n) if(i%3==0) (rest+=dp[n][i+MAXN])%=MOD; 
	printf("%d\\n",rest);
	return 0;
}

以上是关于AGC 043 C,D 题解的主要内容,如果未能解决你的问题,请参考以下文章

AGC 043 C - Giant Graph SG函数 dp 贪心

AtCoder Grand Contest 043 部分题解

AGC 051 D C4 题解

agc026 题解

AGC 053 C Random Card Game 题解

AGC043C Giant Graph