PQ树
PQ树是什么?
PQ树构建
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define re(a) scanf("%d",&a)
#define r(i,a,b) for(int i=a;i<=b;i++)
#define rr(i,a,b) for(int i=a;i>=b;i--)
#define in inline
#define me(a) memset(a,0,sizeof(a))
#define mem(a) memset(a,0x3f,sizeof(a))
#define ll long long
#define db double
using namespace std;
const int N=1e3+7;
const int p=998244353;
int n,fac[N],m,q;
struct node{
vector<int> son;
int op,type;
/*
op:1 无关键点
2 全是关键点
3 一部分是关键点
type: 0 p类点(内部儿子可以随便排列)
1 q类点(内部儿子必须按照正序,或者按照逆序排列)
*/
}t[N<<2];
int cnt=0,rt;
bool ok,vis[N];
in void init(){
cnt=n,rt=++cnt;
r(i,1,n)t[rt].son.push_back(i);
}
in void dfs1(int u){
if(u<=n){
t[u].op=vis[u]?2:1;
return;
}
t[u].op=0;
int len=t[u].son.size()-1;
r(i,0,len){
dfs1(t[u].son[i]);
t[u].op|=t[t[u].son[i]].op;
}
}
in void dfs2(int u,int lim){
/*
lim 0:内部关键点排列无所谓
lim 1:内部关键点全部排列在左
lim 2:内部关键点全部排列在右
*/
if(!ok||t[u].op!=3)return;
vector<int> a[4];
/*
记录u的 不同op的儿子的集合
*/
int len=t[u].son.size()-1;
r(i,0,len){
int v=t[u].son[i];
a[t[v].op].push_back(v);
}
if((lim>0)+a[3].size()>=3){
/*
a[3].size()<=2,(有lim时a[3].size()<=1)有解
其他情况无解
*/
ok=0;
return;
}
if(!lim&&(a[2].size()+a[3].size())==1){
//关键点全在一个子树上
if(!a[2].empty())dfs2(a[2][0],lim);
if(!a[3].empty())dfs2(a[3][0],lim);
return;
}
if(t[u].type){
// q,内部结点有顺序,只需要考虑从左到右枚举
int now=0;
/*
now记录当前状态
0:还从没有关键点出现过(关键点只出现在op为2,3的子树内)
1:上一个位置(若当前是i,则上一个位置是i-1)有关键点
2:上一个位置没关键点,但关键点出现过
*/
vector<int> S;
if(t[t[u].son[0]].op==2||t[t[u].son.back()].op==1){
//先默认lim=2,将关键点向后排列
reverse(t[u].son.begin(),t[u].son.end());
}
int len=t[u].son.size()-1;
r(i,0,len){
int v=t[u].son[i];
if(t[v].op==1){
S.push_back(v);
now+=now==1;
}
else if(t[v].op==2){
S.push_back(v);
now+=!now;
if(now==2)ok=0;
}else{
if(now==2)ok=0;
++now;
dfs2(v,3-now);
S.insert(S.end(),t[v].son.begin(),t[v].son.end());
}
}
if(lim&&now==2)ok=0;
if(lim==1)reverse(S.begin(),S.end());
t[u].son=S;
}else{
int z=-1;
if(a[2].size()==1)z=a[2][0];
else if(a[2].size()>1)z=++cnt,t[z].type=0,t[z].son=a[2];
vector<int> S;//用来存储除了a[1]以外的点
if(!a[3].empty()){
dfs2(a[3][0],2);
S.insert(S.end(),t[a[3][0]].son.begin(),t[a[3][0]].son.end());
}
if(~z)S.push_back(z);
if(a[3].size()>1){
dfs2(a[3][1],1);
S.insert(S.end(),t[a[3][1]].son.begin(),t[a[3][1]].son.end());
}
if(a[1].empty()){
if(lim==1)reverse(S.begin(),S.end());
t[u].type=1;t[u].son=S;
}else{
if(lim){
t[u].son.clear();
t[u].type=1;
z=a[1][0];
if(a[1].size()>1)z=++cnt,t[z].son=a[1],t[z].type=0;
t[u].son.push_back(z);
t[u].son.insert(t[u].son.end(),S.begin(),S.end());
if(lim==1)reverse(t[u].son.begin(),t[u].son.end());
}else{
z=S[0];
if(S.size()>1)z=++cnt,t[z].type=1,t[z].son=S;
t[u].son=a[1],t[u].son.push_back(z);
}
}
}
}
in bool Insert(){
dfs1(rt);ok=1;
dfs2(rt,0);
return ok;
}
in int cal(int u){
if(u<=n)return 1;
int ans=t[u].type?2:fac[t[u].son.size()];
int len=t[u].son.size()-1;
r(i,0,len){
int v=t[u].son[i];
ans=1ll*ans*cal(v)%p;
}
return ans;
}
int main(){
re(n),re(m);fac[0]=1;int x;
r(i,1,n)fac[i]=1ll*fac[i-1]*i%p;
init();
while(m--){
re(q);me(vis);
r(i,1,q)re(x),vis[x]=1;
if(!Insert()){
printf("0\n");
return 0;
}
}
printf("%d\n",cal(rt));
return 0;
}