标签:fir fast now() als merge max long cpp stdout
首先可以理解成一个\(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;
}
如果一个元素不是前缀最大值则一定和前一个元素属于同一堆。
这样就有长度为\(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;
}
标签:fir fast now() als merge max long cpp stdout
原文地址:https://www.cnblogs.com/gary-2005/p/14782368.html