首页 > 其他分享 >CF960G Bandit Blues 题解

CF960G Bandit Blues 题解

时间:2024-06-03 21:14:17浏览次数:21  
标签:return 前缀 trn 后缀 题解 最大值 Bandit int Blues

我不会斯特林数。

CF960G Bandit Blues

给你三个正整数 \(n\),\(a\),\(b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大值的数的个数,求长度为 \(n\) 的排列中满足 \(A = a\) 且 \(B = b\) 的排列个数。\(n \le 10^5\),答案对 \(998244353\) 取模。

一道水黑。

排列,计数题,与数之间的相对大小有关,buff 叠满了。

从大到小插入数。设正在插入 \(u = n - i\),插入它之前有 \(i + 1\) 个空位。

如果 \(i = 0\),也就是插入 \(n\),那么它一定同时是前缀最大值和后缀最大值。

如果 \(i = 1\),那么它能插入两个空位之一,能够成为前缀最大值或后缀最大值,各有 \(1\) 个空位可行。

否则有三种情况:

  1. 插入到当前序列最左边,贡献一个前缀最大值,有 \(1\) 个空位可行。
  2. 插入到当前序列最右边,贡献一个后缀最大值,有 \(1\) 个空位可行。
  3. 插入到其他位置,不贡献,有 \(i - 1\) 个空位。

把 \(i = 0\) 的情况解决掉,令 \(n, a, b\) 都减去 \(1\) 即可。下文默认 \(n, a, b\) 都减了 \(1\)。

那么剩下可以套个多项式。用变量 \(x\) 代表前缀最大值,\(y\) 表示后缀最大值的话,对每个数有三种选择,成为前缀最大值、后缀最大值或者不贡献,所以答案可以表示为:

\[[x^ay^b]\prod _{i = 0} ^{n - 1} (x + y + i) \]

啊?这里有两个变量, \([x^ay^b]\) 我不会操作啊!

注意前缀最大值和后缀最大值互不干扰,也就是每个数都既有前缀最大值的可能也有后缀最大值的可能。

那就能把 \(x\) 和 \(y\) 合并起来了。把操作转化为每个数有两种选择,成为前或后缀最大值,或者不贡献。

然后再用组合数把成为前缀最大值的数挑出来就行了。

所以式子可以化成这个样子:

\[\binom{a + b}{a}[x^{a + b}] \prod _{i = 0} ^{n - 1} (x + i) \]

后半部分用分治 NTT 求一下就行了。见 P6012 或者 AT_abc352_g。(P6012 因为模数不喜欢没写)

#include<iostream>
#include<fstream>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
namespace poly {
	const int G = 3, P = 998244353;
	int Ksm(int u, int v) {
		int ret = 1;
		while (v) {
			if (v & 1) ret = 1ll * ret * u % P;
			u = 1ll * u * u % P, v >>= 1;
		}
		return ret;
	}
	int NTT(vector<int> &A, int N) {
		int d = N >> 1;
		vector<int> trn;
		trn.push_back(0);
		trn.push_back(d);
		for (int w = 2; w <= N; w <<= 1) {
			d >>= 1;
			for (int c = 0; c < w; c ++)
				trn.push_back(trn[c] | d);
		}
		for (int i = 1; i < N; i ++)
			if (trn[i] > i)
				swap(A[i], A[trn[i]]);
		for (int len = 2, M = 1; len <= N; M = len, len <<= 1) {
			int W;
			W = Ksm(G, (P - 1) / len);
			for (int l = 0; l <= N - len + 1; l += len) {
				auto w0 = 1;
				for (int nw = l; nw < l + M; nw ++) {
					int x = A[nw], y = 1ll * w0 * A[nw + M] % P;
					A[nw] = (x + y) % P, A[nw + M] = (x - y + P) % P;
					w0 = 1ll * w0 * W % P;
				}
			}
		}
		return 0;
	}
	vector<int> convolution(vector<int> a, vector<int> b) {
		int n = a.size(), m = b.size();
		if (!n || !m)
			return {};
		int sm = n + m - 1;
		int k = 1;
		while (k < sm)
			k <<= 1;
		a.resize(k), b.resize(k);
		NTT(a, k);
		NTT(b, k);
		for (int i = 0; i < k; i ++)
			a[i] = 1ll * a[i] * b[i] % P;
		NTT(a, k);
		reverse(++ a.begin(), a.end());
		int invk = Ksm(k, P - 2);
		for (int i = 0; i < a.size(); i ++)
			a[i] = 1ll * a[i] * invk % P;
		a.resize(n + m - 1);
		return a;
	}
}
namespace solve1 {
	int n, A, B;
	vector<int> a;
	int sum = 0;
	int ans, C = 1;
	const int modd = 998244353;
	using namespace poly;
	vector<int> calc(vector<int> &a, int l, int r) {
		if (r - l == 1)
			return {a[l], 1};
		int mid = (l + r) >> 1;
		return convolution(calc(a, l, mid), calc(a, mid, r));
	}
	int main() {
		cin >> n >> A >> B;
		if(n == 1){
			if(A == 1 && B == 1) return cout << 1, 0;
			return cout << 0, 0;
		}
		if(A == 0 || B == 0) return cout << 0, 0;
		if (A == 0 || B == 0) return cout << 0, 0;
		if(A + B - 1 > n)
			return cout << 0, 0;
		n --, A --, B --;
		int jc1 = 1, jc2 = 1, jc3 = 1;
		for (int i = 1; i <= A + B; i ++){
			(jc1 *= i) %= P; if(i <= A) (jc2 *= i) %= P;
			if(i <= B) (jc3 *= i) %= P;
		}
		int iv2 = Ksm(jc2, P - 2), iv3 = Ksm(jc3, P - 2);
		int ans = (((jc1 * iv2) % modd) * iv3) % modd;
		for (int i = 0; i < n; i ++) 
			a.push_back(i);
		vector<int> res = calc(a, 0, n);
		cout << ans * res[A + B] % modd;
		return 0;
	}
}
signed main() {
	int T = 1;
	while (T --)
		solve1::main();
	return 0;
}

你说得对,但是我的大常数多项式被同机房的以二分之一常数薄纱。

你说得对,但是我在做完后才知道最后那一坨东西其实是第一类斯特林数。

标签:return,前缀,trn,后缀,题解,最大值,Bandit,int,Blues
From: https://www.cnblogs.com/AzusidNya/p/18229655

相关文章

  • P10550 [THUPC2024] 贸易 题解
    正式场上拿了这题的首\(A\),让队伍不至于无奖而返。思路容易发现题目的买入卖出过程形似一个括号匹配。那么我们可以对每一种类型的物品做括号匹配。若是一个匹配的括号在询问区间内则可以记入答案。就变成了一个二维数点问题。离线树状树组即可。Code#include<bits/stdc......
  • 关于vue关闭页面时去除定时器失效问题解决
    1.先去除页面缓存,这个在路由部分 2.    ......
  • 第十五届蓝桥杯国赛C++B组文字题解
    A:合法密码暴力跑一下即可,坑点是pdf有换行,字母不算字符,最后答案是:400。B:选数概率观察到第二个分数的分母很大,猜测\((a+b+c)\times(a+b+c-1)=20910‬\)发现无整数解,于是考虑到可能被约分了,将\(20910\times2=41820\)最后得到\(a+b+c=105\)然后就......
  • [SDOI2008] Sue 的小球 题解
    题目描述首先将彩蛋按照横坐标从小到大排序,依次标号为\(1\simn\)。显然,\(Sue\)走过一段时间后,走过的点一定属于一段连续区间。所以本题采用区间\(dp\)。不妨先做一个简单转化,由于每个彩蛋初始高度确定,若想让总分最高,就要使扣分最少。所以下面的\(dp\)从扣分最少入手。设......
  • DVWA靶场---csrf遇到的问题解决方法
    1.解决low等级不携带cookie访问诈骗网站:设置---隐私与安全---浏览器隐私---增强型跟踪保护---自定义---cookie---跨站跟踪型cookie。2.解决medium等级referer显示不完整解决方法:在服务器的html上加一段:<metaname="referrer"content="no-referrer-when-downgrade">当从......
  • 力扣2891每日一题题解
    题目:给你一个仅由小写英文字母组成的字符串 s 。如果一个字符串仅由单一字符组成,那么它被称为 特殊 字符串。例如,字符串 "abc" 不是特殊字符串,而字符串 "ddd"、"zz" 和 "f" 是特殊字符串。返回在 s 中出现 至少三次 的 最长特殊子字符串 的长度,如果不存在出......
  • P7311 [COCI2018-2019#2] Maja题解
    [COCI2018-2019#2]Maja题目描述蜜蜂Maja在一个神奇的牧场里为花朵传粉。牧场可用一个\(N\timesM\)的矩阵表示。在第\(i\)行第\(j\)列有\(C_{i,j}\)朵未传粉的花。Maja从位于第\(A\)行第\(B\)列的蜂巢出发,并前往牧场的一些区域后返回。Maja可以在\(1\)步内......
  • rt-thread 系统pm组件在4.1.1版本的无法正常唤醒的问题解决方法
    在老的rt-thread版本系统pm组件调试ok,后来使用4.1.1版本时发现进入低功耗后无法正常唤醒,问题解决路径如下硬件信息:cpu STM32L431CCT6新建系统打开pm组件后也没有drv_pm.c和drv_lptim.c自动添加,需要到系统目下找到并复制到driver目录下C:\RT-ThreadStudio\repo\Extract\R......
  • [题解]UVA11235 Frequent values
    https://www.luogu.com.cn/problem/UVA11235没看到多测调了半天每组数据给定\(n,q\)。接下来给出一个长度为\(n\)的不降序列\(A\)。接下来\(q\)次询问,每次询问给定\(l,r\),求\(A_{l\simr}\)中出现最多的那个数出现了多少次。\(1\len,q\le10^5\)。序列不降,意味着一个数在序......
  • CF1228E Another Filling the Grid 题解
    tag:容斥原题+组合数设F[i]F[i]F[i]表示至少......