首页 > 其他分享 >CodeForces 1830C Hyperregular Bracket Strings

CodeForces 1830C Hyperregular Bracket Strings

时间:2023-05-29 13:56:20浏览次数:58  
标签:return int ll CodeForces Bracket maxn 区间 1830C mod

洛谷传送门

CF 传送门

每一步思路都非常自然的题。

考虑先从一些简单的 case 入手。

1. \(k = 0\)

设 \(g_i\) 为长度为 \(i\) 的合法括号串数。显然 \(\forall i \nmid 2, g_i = 0\)。对于偶数的 \(i\),这是一个经典问题,\(g_i\) 就是第 \(\frac{i}{2}\) 项卡特兰数,因此 \(g_i = \binom{i}{\frac{i}{2}} - \binom{i}{\frac{i}{2} - 1}\)。

2. 区间互不相交

考虑把这些区间删去,那么剩下的括号也要构成合法括号串。设 \(len_i\) 为第 \(i\) 个区间的长度,\(r\) 为区间长度总和,答案即为 \(g_{n - r} \times \prod\limits_{i=1}^k g_{len_i}\)。

3. 区间两两不交或包含

考虑建树,每个区间向包含它且最短的区间连边,那么会构成一片森林。设 \(f_u\) 为第 \(u\) 个区间内部的方案数,\(r_u\) 为 \(\sum\limits_{v \in son_v} len_v\),那么由互不相交的 case 我们得到 \(f_u = g_{len_u - r_u}\prod\limits_{v \in son_u} f_v\)。

4. 区间可能相交但不包含

举个例子,如果 \([3, 6]\) 和 \([5, 8]\) 都是合法括号串,画出折线图,可以发现相当于 \([3, 4], [5, 6], [7, 8]\) 都是合法括号串。也就是我们要把相交的部分分裂出来。考虑异或哈希,每个区间赋一个随机权值,把区间里的每一个数都异或上这个权值,最后把每个权值第一次出现的位置最后一次出现的位置拎出来作为新的区间。显然它们只会两两不交或包含了。

一步一步推下来,都挺自然的。

复杂度是线性对数。

code
// Problem: C. Hyperregular Bracket Strings
// Contest: Codeforces - Codeforces Round 875 (Div. 1)
// URL: https://codeforces.com/contest/1830/problem/C
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 1000100;
const int logn = 22;
const int N = 1000000;
const ll mod = 998244353;

inline ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll n, m, fac[maxn], ifac[maxn], f[maxn];
int g[maxn][logn], e[maxn], pre[maxn], lst[maxn], lg[maxn], fa[maxn];
ull b[maxn], c[maxn], d[maxn];
vector<int> G[maxn];
mt19937_64 rnd(time(NULL));

struct node {
	ll l, r;
	node(ll a = 0, ll b = 0) : l(a), r(b) {}
} a[maxn];

void init() {
	fac[0] = 1;
	for (int i = 1; i <= N; ++i) {
		fac[i] = fac[i - 1] * i % mod;
	}
	ifac[N] = qpow(fac[N], mod - 2);
	for (int i = N - 1; ~i; --i) {
		ifac[i] = ifac[i + 1] * (i + 1) % mod;
	}
	for (int i = 2; i <= N; ++i) {
		lg[i] = lg[i >> 1] + 1;
	}
}

inline ll C(ll n, ll m) {
	if (n < m || n < 0 || m < 0) {
		return 0;
	} else {
		return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
	}
}

inline ll calc(ll n) {
	if (n & 1) {
		return 0;
	}
	n /= 2;
	return (C(n * 2, n) - C(n * 2, n - 1) + mod) % mod;
}

inline int qmax(int l, int r) {
	int k = lg[r - l + 1];
	return max(g[l][k], g[r - (1 << k) + 1][k]);
}

void dfs(int u) {
	f[u] = 1;
	ll len = a[u].r - a[u].l + 1;
	for (int v : G[u]) {
		dfs(v);
		len -= a[v].r - a[v].l + 1;
		f[u] = f[u] * f[v] % mod;
	}
	f[u] = f[u] * calc(len) % mod;
}

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 0; i <= n + 1; ++i) {
		b[i] = c[i] = d[i] = 0;
	}
	for (int i = 1; i <= m; ++i) {
		scanf("%lld%lld", &a[i].l, &a[i].r);
		ull x = rnd(), y = rnd(), z = rnd();
		b[a[i].l] ^= x;
		b[a[i].r + 1] ^= x;
		c[a[i].l] ^= y;
		c[a[i].r + 1] ^= y;
		d[a[i].l] ^= z;
		d[a[i].r + 1] ^= z;
	}
	sort(a + 1, a + m + 1, [&](node a, node b) {
		return a.l < b.l || (a.l == b.l && a.r < b.r);
	});
	if (n & 1) {
		puts("0");
		return;
	}
	for (int i = 1; i <= m; ++i) {
		if ((a[i].r - a[i].l + 1) & 1) {
			puts("0");
			return;
		}
	}
	for (int i = 1; i <= n; ++i) {
		b[i] ^= b[i - 1];
		c[i] ^= c[i - 1];
		d[i] ^= d[i - 1];
	}
	map< pair< ull, pair<ull, ull> >, int > mp;
	int tot = 0;
	for (int i = 1, j = 1; i <= n; i = (++j)) {
		while (j < n && (b[j + 1] == b[i] && c[j + 1] == c[i] && d[j + 1] == d[i])) {
			++j;
		}
		// printf("i, j: %d %d %llu %llu %llu\n", i, j, b[i], c[i], d[i]);
		auto t = make_pair(b[i], make_pair(c[i], d[i]));
		if (mp.find(t) == mp.end()) {
			mp[t] = ++tot;
		}
		int val = mp[t];
		for (int k = i; k <= j; ++k) {
			e[k] = val;
		}
	}
	for (int i = 1; i <= tot; ++i) {
		pre[i] = lst[i] = 0;
	}
	for (int i = 1; i <= n; ++i) {
		if (!pre[e[i]]) {
			pre[e[i]] = i;
		}
		lst[e[i]] = i;
	}
	// for (int i = 1; i <= tot; ++i) {
		// printf("i: %d %lld %lld\n", i, pre[i], lst[i]);
	// }
	for (int i = 1; i <= tot; ++i) {
		a[i] = node(pre[i], lst[i]);
	}
	sort(a + 1, a + tot + 1, [&](node a, node b) {
		return a.l < b.l || (a.l == b.l && a.r < b.r);
	});
	for (int i = 0; i <= tot + 1; ++i) {
		f[i] = 0;
		vector<int>().swap(G[i]);
	}
	for (int i = 1; i <= tot; ++i) {
		g[i][0] = a[i].r;
		// printf("i: %d %lld %lld\n", i, a[i].l, a[i].r);
	}
	for (int j = 1; (1 << j) <= tot; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= tot; ++i) {
			g[i][j] = max(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]);
		}
	}
	for (int i = 1; i <= tot; ++i) {
		int l = 1, r = i - 1, pos = -1;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (qmax(mid, i - 1) > a[i].r) {
				pos = mid;
				l = mid + 1;
			} else {
				r = mid - 1;
			}
		}
		fa[i] = pos;
		if (pos != -1) {
			G[pos].pb(i);
		}
	}
	ll ans = 1, t = n;
	for (int i = 1; i <= tot; ++i) {
		if (fa[i] == -1) {
			dfs(i);
			t -= a[i].r - a[i].l + 1;
			ans = ans * f[i] % mod;
		}
	}
	ans = ans * calc(t) % mod;
	printf("%lld\n", ans);
}

int main() {
	init();
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:return,int,ll,CodeForces,Bracket,maxn,区间,1830C,mod
From: https://www.cnblogs.com/zltzlt-blog/p/17440207.html

相关文章

  • Codeforces Round #875 (Div. 2)
    CodeforcesRound#875(Div.2)bilibili:CodeforcesRound#875(Div.2)实况|完成度[4.01/6]A#include<bits/stdc++.h>usingll=longlong;usinguint=unsigned;usingnamespacestd;intTestCase;signedmain(){ for(scanf("%d",&......
  • Codeforces Round 875 (Div. 2) A~D
    CodeforcesRound875(Div.2)A~DA.TwinPermutations构造\(a[i]+b[i]=n+1\)voidwork(){ intn; cin>>n; rep(i,1,n){ intx; cin>>x; cout<<n-x+1<<""; } cout<<endl;}B.Arraymerging......
  • P9356 「SiR-1」Bracket 题解
    P9356「SiR-1」Bracket题解首先我们来先考虑一下如何计算一个给定的\(f(s[1,n])\)。一般括号序列的题目都是比较套路的将\(\texttt{(}\)赋值为\(1\),将\(\texttt{)}\)赋值为\(-1\),然后求一下前缀和记为\(sum_i\),那么一个括号序列是合法的当且仅当\(\foralli\in[1,n],......
  • Educational Codeforces Round 149 (Rated for Div. 2)(A~F)
    A.GrasshopperonaLine题意:给出n,k,从0开始,每次可以加一个数,最快到达n需要,输出首先跳几次,然后每次跳多少,限制只有一个跳的长度不能整除k。分析:n%k,有余直接跳,没余数,先跳一个,再跳剩余的长度。代码:#include<cstring>#include<iostream>#include<algorithm>usingnamespac......
  • Codeforces 1444E - Finding the Vertex
    非常神秘的一道题,当之无愧的*3500。首先考虑转化题意。考虑一种决策树,由于我们每次问一条边之后,相当于会根据信息删掉两个连通块中的一个,因此一种决策树实际上对应了原树的一棵边分树。而为了让最坏情况下的询问次数最少,我们目标实际上是最小化边分树的深度。考虑借鉴P5912JA......
  • Educational Codeforces Round 149 (Rated for Div. 2) 题解
    https://codeforces.com/contest/1837https://codeforces.com/contest/1837/problems利益相关:上紫祭。真的不要以为这道题放在F就不敢做。压线过题的感觉真好。ABC题都过水,就不写了。代码丢在这里:A:https://codeforces.com/contest/1837/submission/207156920B:https:......
  • CodeForces 1107B Digital root(找规律)
    传送门每个数字都有个数位和,就是把数字的每一位相加直到数位和是一个个位数。然后题目就要你求第K个数位和为X的数字是多少。写一些数字出来就很容易发现规律了可以看出每一竖列的数位和是相等的,然后就找到规律是9*(k-1)+x,注意数据范围是1e12,是longlong,然后就这么多,就可以直......
  • CodeForces 1107A Digits Sequence Dividing(思维)
    传送门唉,题目讲的天花乱坠的,花里胡哨,一上来真是把我唬住了。愣了半天也没看出来到底咋做,后来借助翻译明白了这个题就是让你把一串字符分成两串,然后第一串要比第二串小,就这样,然后又是个SpecialJudge。做的时候就把第一个数作为第一个串,然后串长如果为2,就判断一下后面的串要比第一个......
  • CodeForces 1108B Divisors of Two Integers(思维)
    传送门题目大意就是给你由X,Y两个数的所有因子(包括一和数本身)组成的序列,然后通过这个序列找出这两个数。由此可见,序列里最大的数一定是X或Y其中的一个,然后我们的任务就是找另一个了,我找的是剩下的因子里不能被已找到的那个数整除的数中最大的数,且没有和这个数相同的数。#include<std......
  • Educational Codeforces Round 63 (Rated for Div. 2) A,B,C
    A.ReverseaSubstring传送门就是找不满足升序排列的字母,输出就行了。#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintmaxn=3e5+10;chars[maxn];intmain(){#ifndefONLINE_JUDGEfreopen("in","r",stdin);#endif//ONL......