「CF1831E」Hyperregular Bracket Strings 题解

本文网址:https://www.cnblogs.com/zsc985246/p/17565768.html ,转载请注明出处。

前言

没见过的套路,写篇题解记录一下。

传送门

「CF1831E」Hyperregular Bracket Strings

题目大意

给定 nk 个区间 [li,ri],你需要找出满足以下条件的合法括号序列个数:

  • 序列的长度为 n

  • i[1,k],区间 [li,ri] 的括号序列合法。

答案对 998244353 取模。

1n3×105,0k3×105,1lirin

思路

看到括号序列,直接把 ( 看成 +1) 看成 1

我们知道,对于一个长度为 n 的合法括号序列,令它的前缀和为 s,那么 s0=sn=0,i[1,n1],si0

那么我们就将题目转化为如下:

求由 +11 组成且满足 k 条限制的长度为 n 的合法序列 s 个数,其中第 i 条限制为区间 [li,ri] 合法。

一个区间 [l,r] 合法当且仅当 sl1=sr,i[l,r1],sisr

首先如果 [li,ri][lj,rj] 不相交,可以分开计算答案,所以我们只需要处理相交的情况

如果 liljrjri,即两个区间为包含关系,发现区间 [li,lj)(rj,ri] 的括号序列拼接起来一定合法。

如果 liljrirj,即两个区间不互相包含,根据上方的结论,slj1sli1=sri,srislj1=srj,可以推出 sli1=sri=slj1=srj,也就可以推出区间 [lj,ri] 也必须合法。

这个时候我们就可以使用套路。我们给每个区间随机分配一个权值,一个位置的权值就是包含这个位置的区间的权值异或和

这可以使用前缀异或和维护。这样做的好处是所有权值相同的位置拿出来一定构成合法序列,并且每个位置恰好属于一个合法序列。

括号序列有一个结论:长度为 n 的合法括号序列的方案数是卡特兰数第 n2 项。对每个权值都算一次卡特兰数,相乘即为答案。

代码实现

不建议使用 rand 和 mt19937,因为值域不够大,容易冲突。除了使用代码中的 mt19937_64 之外,还可以使用 hash。

#include<bits/stdc++.h> 
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;
const ll p=998244353;
//组合数模板
ll ksm(ll a,ll b){ll bns=1;while(b){if(b&1)bns=bns*a%p;a=a*a%p;b>>=1;}return bns;}
ll jc[N],inv[N];
void init(ll n){jc[0]=1;For(i,1,n)jc[i]=jc[i-1]*i%p;inv[n]=ksm(jc[n],p-2);Rep(i,n-1,0)inv[i]=inv[i+1]*(i+1)%p;}
ll C(ll n,ll m){if(m<0)return 1;if(n<m)return 0;return jc[n]*inv[m]%p*inv[n-m]%p;}
//随机数初始化
mt19937_64 rnd(random_device{}());
uniform_int_distribution<ll>dist(0,LLONG_MAX);

ll n,m,k;
ll a[N];

ll Catalan(ll x){//卡特兰数第x项
	return (C(2*x,x)-C(2*x,x-1)+p)%p;
}

void mian(){
	
	scanf("%lld%lld",&n,&k);
	For(i,0,n)a[i]=0;
	For(i,1,k){
		ll l,r;
		scanf("%lld%lld",&l,&r);
		//分配随机权值
		ll t=dist(rnd);
		a[l]^=t,a[r+1]^=t;
	}
	map<ll,ll>vis;
	For(i,1,n)vis[a[i]^=a[i-1]]++;//统计各个权值的数量
	ll ans=1;
	for(auto i:vis){
		if(i.second&1)ans=0;//不合法
		else ans=ans*Catalan(i.second/2)%p;//计算答案
	}
	printf("%lld\n",ans);
	
}

int main(){
	init(1e6);//组合数预处理
	int T=1;
	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

尾声

如果你发现了问题,你可以直接回复这篇题解

如果你有更好的想法,也可以直接回复!

posted @   zsc985246  阅读(255)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示