首页 > 其他分享 >AtCoder Grand Contest 058 D Yet Another ABC String

AtCoder Grand Contest 058 D Yet Another ABC String

时间:2023-07-08 11:00:41浏览次数:51  
标签:AtCoder ABC String texttt 容斥 插入 3i 起点

洛谷传送门

AtCoder 传送门

Orz H6_6Q,感谢他给我们带来了这道容斥好题。

这个东西看起来很不好搞。可以尝试容斥。

但是我们要容斥啥?钦定 ABC 不出现,其他任意?感觉还是很难算。

观察不合法子串,发现它们很有特点。如果我们钦定 \(\texttt{A}\) 为 \(0\),\(\texttt{B}\) 为 \(1\),\(\texttt{C}\) 为 \(2\),那么不合法串相当于 \(S_i + 1 \equiv S_{i + 1} \pmod 3\)。

并且我们还发现一个性质,如果 \(S_{i \sim i + 2}\) 不合法,即 \(S_{i \sim i + 2}\) 为 \(\texttt{ABC}, \texttt{BCA}, \texttt{CAB}\) 中的其中一个,并且 \(S_{i + 1 \sim i + 3}\) 也不合法,那么 \(S_{i \sim i + 3}\) 也不合法。因为由上面不合法子串的特点我们得到了这个东西具有传递性

现在我们尝试考虑每个极长不合法子串。发现它们既有起点又有终点。我们尝试容斥起点。即我们钦定有 \(i\) 个极长不合法子串的起点。

极长不合法子串的长度还不确定,但是我们发现只要钦定它们的长度 \(\ge 3\),即每个字母至少出现一次即可。

于是我们现在相当于有 \(a - i\) 个 \(\texttt{A}\),\(b - i\) 个 \(\texttt{B}\),\(c - i\) 个 \(\texttt{C}\) 是自由的。我们需要把它排成一个初始串。这部分的方案数由多重组合数易得为 \(\frac{(a + b + c - 3i)!}{(a - i)! (b - i)! (c - i)!}\)。

然后,我们需要把 \(i\) 个串,每个都是 \(\texttt{ABC}, \texttt{BCA}, \texttt{CAB}\) 中之一的串插入一开始排成的那个初始串。如果插入的位置在开头,那么 \(3\) 个串都可以被选择插入;如果在中间,因为我们容斥的是极长不合法子串的起点,也就是说它们必须是起点,其他的可以是也可以不是,所以我们不能接在字母序上一个字母后面,导致这个字母成为了起点。具体一点就是,\(\texttt{ABC}\) 不能接在 \(\texttt{C}\) 后面,\(\texttt{BCA}\) 不能接在 \(\texttt{A}\) 后面,\(\texttt{CAB}\) 不能接在 \(\texttt{B}\) 后面。我们发现无论插入位置前一个字母是什么,插入的串都有 \(2\) 种选择。

现在考虑统计插入初始串并选择是 \(\texttt{ABC}, \texttt{BCA}\) 还是 \(\texttt{CAB}\) 的方案数。分类讨论。

  • 如果没有串被放到开头,那么初始串有 \(a + b + c - 3i\) 个空隙。我们需要把 \(i\) 个起点分配成 \(a + b + c - 3i\) 份,每份可以为空,然后再按份对应地插入到这么多空隙中,再乘上选择插入的是哪个串的方案数。于是这部分的方案数就是 \(\binom{i + a + b + c - 3i - 1}{a + b + c - 3i - 1} \times 2^i\)。
  • 如果有 \(1\) 个串被放到开头,那么我们先放一个起点到开头,那么现在的串有 \(a + b + c + 3i + 1\) 个空隙,并且我们还剩 \(i - 1\) 个起点。由上面的推法可得这部分的方案数为 \(\binom{i - 1 + a + b + c - 3i}{a + b + c - 3i} \times 2^{i - 1} \times 3\)。

然后,我们把排成初始串的方案数,和上面统计的插入并选择的方案数相乘,再乘上容斥系数 \((-1)^i\)。对于每个 \(i \in [0, \min(a, b, c)]\) 求和,即是答案。

启发:容斥的东西可以是非常规的,不一定是题目直接给出的东西。

code
// Problem: D - Yet Another ABC String
// Contest: AtCoder - AtCoder Grand Contest 058
// URL: https://atcoder.jp/contests/agc058/tasks/agc058_d
// Memory Limit: 1024 MB
// Time Limit: 2000 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 double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

/*
很强的容斥!

考虑一个极长的不合法子串连续段 `ABCABC...`,我们考虑容斥**每个连续段的起点**。

设钦定了 $x$ 个位置**是起点**,其他位置**随意**。对于一个起点的插入,要满足**它前面的字符不是它的前一位**。例如如果要插入 `ABC`,它前一位不能是 `C`,否则起点就要往前移。
*/

const int maxn = 5000100;
const int N = 5000000;
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 a, b, c, fac[maxn], ifac[maxn], pw[maxn];

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

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;
	}
}

void solve() {
	scanf("%lld%lld%lld", &a, &b, &c);
	ll ans = 0;
	for (int i = 0; i <= min({a, b, c}); ++i) {
		ll t = fac[a + b + c - i * 3] * ifac[a - i] % mod * ifac[b - i] % mod * ifac[c - i] % mod;
		ll res = C(i + a + b + c - i * 3 - 1, a + b + c - i * 3 - 1) * pw[i] % mod;
		if (i) {
			res = (res + C(i - 1 + a + b + c - i * 3, a + b + c - i * 3) * pw[i - 1] % mod * 3 % mod) % mod;
		}
		ans = (ans + ((i & 1) ? mod - 1 : 1) * res % mod * t % mod) % mod;
	}
	printf("%lld\n", ans);
}

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

标签:AtCoder,ABC,String,texttt,容斥,插入,3i,起点
From: https://www.cnblogs.com/zltzlt-blog/p/17536892.html

相关文章

  • [oeasy]python0071_字符串类型_str_string_下标运算符_中括号
    回忆上次内容上次分辨了静态类型语言动态类型语言 python属于对类型要求没有那么严格的动态类型语言 对初学者很友好不过很多时候也容易弄不清变量类型 直接修改代码增强程序的可读性把变量的类型明确标......
  • AtCoder Beginner Contest 308 题解
    https://atcoder.jp/contests/abc308/tasks_printA-NewScheme过水已隐藏。#include<bits/stdc++.h>#include<ext/pb_ds/assoc_container.hpp>#include<ext/pb_ds/tree_policy.hpp>#include<ext/pb_ds/hash_policy.hpp>usingnamespacestd;u......
  • C++黑马程序员——P193-196. string容器 字符串比较,字符存取,字符串插入和删除,子串
    P193.string容器——字符串比较P194....——字符存取P195....——字符串插入和删除P196....——子串获取P193.字符串比较 ——————————————————————————————————————————————————————————1//字符......
  • AtCoder Beginner Contest 264 ABCDE
    AtCoderBeginnerContest264A-"atcoder".substr()ProblemStatement题意:截取字符串atcoder的[L,R]一段并输出。Solution题解:用string.substr直接写#include<bits/stdc++.h>usingnamespacestd;intmain(){ strings="?atcoder"; intl,r; cin&......
  • C++黑马程序员——P189-192. string容器 构造函数,赋值,拼接,查找和替换
    P189.string容器——构造函数P190....——赋值操作P191....——字符串拼接P192....——字符串查找和替换P189.构造函数———————————————————————————————————————————————————————————————......
  • 多线程知识:三个线程如何交替打印ABC循环100次
    本文博主给大家讲解一道网上非常经典的多线程面试题目。关于三个线程如何交替打印ABC循环100次的问题。下文实现代码都基于Java代码在单个JVM内实现。问题描述给定三个线程,分别命名为A、B、C,要求这三个线程按照顺序交替打印ABC,每个字母打印100次,最终输出结果为:ABCABC.......
  • API和String字符串介绍
    API1、如何使用Java已经写好的东西(方法,类)API(Applicationprogramminginterface):应用程序编程接口简单理解:API就是别人已经写好了的东西,我们不需要自己编写,直接使用即可啊Publicstaticvoidmain(String[]args){ Randomr=newRandom(); intnumber=r.nextInt(100);}......
  • AtCoder Regular Contest 163
    A只需暴力判断能否分成两部分即可。时间复杂度\(\mathcal{O}(n^2)\)。B肯定是选值域连续的一段数操作,排序后枚举区间即可。时间复杂度\(\mathcal{O}(n\logn)\)。C场上降智了15min,我是什么shaber啊。注意到\(1=\frac1n+\sum_{i<n}\frac{1}{i(i+1)}\),但......
  • Python如何实现docstring
    docPython语言从排版上看强制要求了一些书写规范,算是强制赋予了每个程序员一个"代码洁癖"。作为规范的一部分,可以在在类函数的开始增加注释,并且语言本身为这种注释做了"背书":可以通过help展示这个帮助文档的内容。这个本来是Python一个很细小的功能,也是一个很有意思的语法糖(因......
  • AtCoder Beginner Contest 304
    A:1#include<cstdio>2#include<cstring>3#include<algorithm>4#include<iostream>5#include<string>6#include<vector>7#include<stack>8#include<bitset>9#include<cstdlib>10#include......