CF1830C Hyperregular Bracket Strings

CF1830C Hyperregular Bracket Strings

对于括号序列 \(s\),我们通常使用折线图来描述。

我们设 \(n+1\) 个点,第 \(i-1\) 个点到第 \(i\) 个点会经过 \(s_i\)

考虑对于限制 \([l_i,r_i]\)\([l_j,r_j]\),倘若两者有交,我们可以寻找更多性质。

我们先假定 \(l_i \leq l_j \leq r_i \leq r_j\),容易发现如下性质:

image

注意到 \(l_i - 1\)\(r_i\) 高度相同,\(l_j - 1\)\(r_j\) 高度相同。

同时 \(l_j - 1\) 的高度一定不小于 \(l_i - 1\)\(r_i\) 的。

同理 \(r_j\) 的高度一定不小于 \(l_j - 1\)\(r_i\) 的,所以两者高度相等。

根据上述内容,我们将区间拆分成了三段合法括号序列。

接下来考虑两个区间包含,容易发现此时去掉中间的部分两边拼起来可以形成括号序列。

考虑使用异或哈希,每次给 \([l_i,r_i]\) 整体异或一个随机数,我们声称:

  • 值相同的所有位置构成合法括号序列

这是有道理的,复杂度 \(O(n \log n)\)

//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<ctime>
#include<map>
#include<random>
using namespace std;
const long long mod=998244353;
mt19937_64 rnd(time(0));
unsigned long long sum[300010];
long long inv(long long num){
	long long pre=mod-2,ans=1;
	while(pre){
		if(pre&1){
			ans=ans*num%mod;
		}
		num=num*num%mod;
		pre>>=1;
	}
	return ans;
}
long long fact[300010],invfact[300010];
void init(int n){
	fact[0]=invfact[0]=1;
	for(int i=1;i<=n;i++){
		fact[i]=fact[i-1]*i%mod;
		invfact[i]=invfact[i-1]*inv(i)%mod;
	}
}
void solve(){
	int n,k;
	scanf("%d %d",&n,&k);
	init(n);
	for(int i=0;i<=n;i++){
		sum[i]=0;
	}
	while(k--){
		int l,r;
		scanf("%d %d",&l,&r);
		unsigned long long v=rnd();
		sum[l]^=v;
		sum[r+1]^=v;
	}
	map<unsigned long long,int> dict;
	for(int i=1;i<=n;i++){
		sum[i]^=sum[i-1];
		dict[sum[i]]++;
	}
	long long ans=1;
	for(map<unsigned long long,int> :: iterator iter=dict.begin();iter!=dict.end();iter++){
		long long pre;
		if(iter->second%2==0){
			pre=((fact[iter->second]*invfact[iter->second/2]%mod*invfact[iter->second/2]%mod-fact[iter->second]*invfact[iter->second/2-1]%mod*invfact[iter->second/2+1]%mod)%mod+mod)%mod;
		}
		else{
			pre=0;
		}
		ans=ans*pre%mod;
	}
	printf("%lld\n",ans);
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		solve();
	} 
	return 0;
}
posted @ 2026-03-23 11:53  Oken喵~  阅读(2)  评论(0)    收藏  举报