[CF1111D]Destroy the Colony

题目大意:有一个长度为$n(n\leqslant10^5,n=0\pmod2)$的字符串,字符集大小为$52$,有$q(q\leqslant10^5)$次询问,每次询问第$x,y$个字符在这个字符串的同一侧,并且所有相同字符在字符串的同一侧的方案数。

题解:因为字符集大小只有$52$,所以本质不同的询问只有$52\times52$种,预处理。

发现若确定了左右各放那几种字符后方案数是一定的,为$\dfrac{\left(\dfrac n2!\right)^2}{\prod\limits_{i=1}^{52}cnt_i!}$,$cnt_i$表示字符$i$出现次数。只需要求出左边可以放的字符种类,乘上这个数就是答案。

考虑$DP$,若正常的做,不加入$x,y$两种字符,复杂度是$O(52^3\times n)$,不可以通过。发现最多只有两种字符不加入,可以退背包,就倒着做背包部分即可,复杂度$O(52^2\times n)$

卡点:阶乘逆元算成了每个数的逆元

C++ Code:

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <iostream>
#define maxn 100010
#define N 52
const int mod = 1e9 + 7;
inline void reduce(int &x) { x += x >> 31 & mod; }

std::string __s;
long long C;
int n, nn, q;
int fac[maxn], inv[maxn];
int s[maxn], cnt[N];

int f[maxn], g[maxn], ans[N][N];
int solve(int x, int y) {
	std::copy(f, f + nn + 1, g);
	for (int i = cnt[x]; i <= nn; ++i) reduce(g[i] -= g[i - cnt[x]]);
	for (int i = cnt[y]; i <= nn; ++i) reduce(g[i] -= g[i - cnt[y]]);
	return 2 * g[nn] % mod;
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	std::cin >> __s; n = __s.length(), nn = n >> 1;
	for (int i = 0; i < n; ++i) {
		s[i + 1] = islower(__s[i]) ? __s[i] - ‘a‘ : __s[i] - ‘A‘ + 26;
		++cnt[s[i + 1]];
	}
	fac[0] = fac[1] = inv[0] = inv[1] = 1;
	for (int i = 2; i <= nn; ++i) {
		fac[i] = static_cast<long long> (fac[i - 1]) * i % mod;
		inv[i] = static_cast<long long> (mod - mod / i) * inv[mod % i] % mod;
	}
	for (int i = 2; i <= nn; ++i) inv[i] = static_cast<long long> (inv[i - 1]) * inv[i] % mod;
	C = static_cast<long long> (fac[nn]) * fac[nn] % mod;
	for (int i = 0; i < N; ++i) C = C * inv[cnt[i]] % mod;

	f[0] = 1;
	for (int i = 0; i < N; ++i) if (cnt[i])
		for (int j = nn; j >= cnt[i]; --j) reduce(f[j] += f[j - cnt[i]] - mod);
	for (int i = 0; i < N; ++i) if (cnt[i]) {
		for (int j = i + 1; j < N; ++j) if (cnt[j])
			ans[i][j] = ans[j][i] = solve(i, j);
		ans[i][i] = f[nn];
	}

	std::cin >> q;
	while (q --> 0) {
		static int x, y;
		std::cin >> x >> y; x = s[x], y = s[y];
		std::cout << C * ans[x][y] % mod << ‘\n‘;
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/Memory-of-winter/p/10357940.html

时间: 2024-10-10 09:40:27

[CF1111D]Destroy the Colony的相关文章

Codeforces1111D Destroy the Colony 退背包+组合数

Codeforces1111D 退背包+组合数 D. Destroy the Colony Description: There is a colony of villains with several holes aligned in a row, where each hole contains exactly one villain. Each colony arrangement can be expressed as a string of even length, where the

CodeCraft-19 and Codeforces Round #537 (Div. 2) - D. Destroy the Colony(动态规划+组合数学)

Problem  CodeCraft-19 and Codeforces Round #537 (Div. 2) - D. Destroy the Colony Time Limit: 2000 mSec Problem Description Input Output For each question output the number of arrangements possible modulo 10^9+7. Sample Input abba21 41 2 Sample Output

[CF1111D] Destory the Colony

Portal 大致题意: 给定一个偶数长度(\(n \leq 10 ^ 5\))的字符串, 只包含大小写字母. 有q(\(q \leq 10 ^ 5\))次询问, 每次指定两个位置, 要求通过交换字符, 使这两个类型的字符在串同一边并且对于其他类型的字符, 不能跨过串的中线(也就是说必须在一边, 但是可以不跟指定的字符一边), 求方案数模\(1e9 + 7\) Solution 这个题目很像atcoder啊 考虑去掉多余的状态, 事实上只有\(52 ^ 2 = 2704\)种状态, 其他的询问都

CF - 1111D Destroy the Colony DP

题目传送门 题意: 这个题目真的是最近遇到的最难读. 有一个长度n的字符串,每一位字符都代表的是该种种类的敌人. 现在如果一个序列合法的话,就是同一种种类的敌人都在字符串的左半边或者右半边. 现在有q次询问,现在问你将 s[x] 和 s[y] 的敌人都放在同一边的合法方案数是多少. 题解: 首先如果划分组之后,那么答案就是,m! * m! * 2/ (c1! * c2! * c3! .... ) 然后对于每一组来说就是 这个值是一定的. 然后就是需要求这个分组方案数. 对于分组方案数,可以通过背

CodeCraft-19 and Codeforces Round #537 (Div. 2)

A. Superhero Transformation 水题,注意两个字符串可能长度不相等. #include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; map<char ,int >m; int main(){ m['a']=m['e']=m['i']=m['o']=m['u']=1;

codeforces contest 1111

A. Superhero Transformation 题意: 元音和元音,辅音和辅音字母之间可以互相转换,问两个字符串是否想同: 题解:直接判断即可: 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1010; 4 char s[N]; 5 int n,m,vis1[N],vis2[N]; 6 int judge(char x){return x=='a'||x=='e'||x=='i'||x=='o'||x=='

CodeCraft-19 and Codeforces Round #537 (Div. 2) 题解

传送门 D. Destroy the Colony 首先明确题意:除了规定的两种(或一种)字母要在同侧以外,其他字母也必须在同侧. 发现当每种字母在左/右边确定之后,方案数就确定了,就是分组的方案数乘\(\frac{((n/2)!)^2}{\prod cnt_i!}\). 分组的方案数考虑DP,设\(dp_{i,j}\)为前\(i\)个字母,占了左边\(j\)个位置的方案数,则有: \[ dp_{i,j}=dp_{i-1,j-cnt_i}+dp_{i-1,j} \] 当\(i\)是指定字母时特判

Delphi的对象注销方法Destroy和free

当您使用完对象后,您应该及时撤销它,以便把这个对象占用的内存释放出来.您可以通过调用一个注销方法来撤销您的对象,它会释放分配给这个对象的内存. Delphi的注销方法有两个:Destroy和Free.Delphi建议使用Free,因为它比Destroy更为安全,同时调用Free会生成效率更高的代码. 您可以用下列的语句释放用完的Employee对象: Employee.Free; 和Create方法一样,Free方法也是TEmployee从TObject中继承过来的.把您的注销放在try…fin

Command to destroy cluster

Command to destroy cluster: Get-Cluster Cluster1 | Remove-Cluster -Force-CleanupAD https://technet.microsoft.com/en-us/library/ee461005.aspx