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 \(i\)-th character of the string represents the type of villain in the \(i\)-th hole.

Iron Man can destroy a colony only if the colony arrangement is such that all villains of a certain type either live in the first half of the colony or in the second half of the colony.

His assistant Jarvis has a special power. It can swap villains of any two holes, i.e. swap any two characters in the string; he can do this operation any number of times.

Now Iron Man asks Jarvis \(q\) questions. In each question, he gives Jarvis two numbers \(x\) and \(y\). Jarvis has to tell Iron Man the number of distinct colony arrangements he can create from the original one using his powers such that all villains having the same type as those originally living in \(x\)-th hole or \(y\)-th hole live in the same half and the Iron Man can destroy that colony arrangement.

Two colony arrangements are considered to be different if there exists a hole such that different types of villains are present in that hole in the arrangements.

Input:

The first line contains a string \(s\) (\(2 \le |s| \le 10^{5}\)), representing the initial colony arrangement. String \(s\) can have both lowercase and uppercase English letters and its length is even.

The second line contains a single integer \(q\) (\(1 \le q \le 10^{5}\)) — the number of questions.

The \(i\)-th of the next \(q\) lines contains two integers \(x_i\) and \(y_i\) (\(1 \le x_i, y_i \le |s|\), \(x_i \ne y_i\)) — the two numbers given to the Jarvis for the \(i\)-th question.

Output

For each question output the number of arrangements possible modulo \(10^9+7\).

Sample Input:

abba

2

1 4

1 2

Sample Output:

2

0

Sample Input:

AAaa

2

1 2

1 3

Sample Output:

2

0

Sample Input:

abcd

1

1 3

Sample Output:

8

题目链接

题解:

有一个长为偶数\(n\)的字符串,只含大小写字母,你可以任意打乱这个字符串,\(q\)次询问,每次给一个\(x, y\),询问满足所有相同字符均在同一半侧(左半侧或右半侧)并且原串中\(x和y\)这两个位置的字符也均在同一侧的字符串种类数

首先考虑没有\(xy\)限制的情况

假设你选择了\(a\)种字符,出现次数之和为\(n/2\),那这\(a\)个字符在左边的方案数为\((n/2)! / (\prod_{i=1}^acnt_i!)\), 右边的字符方案数为\((n / 2)! / (所有不在a中的字符的出现次数的阶乘的累乘积)\), 总方案数为\(2*(n/2)!*(n/2)!/ (\prod_{ch = 'a'}^{'z'}cnt_{ch}! * \prod_{ch = 'A'} ^ {'Z'}cnt_{ch}!)\),这个数再乘以任选若干个字符总出现次数为\(n/2\)的方案数的一半就是答案(一半是因为选自身和自身的补集会重复计算)

现在考虑\(xy\),之前的选定\(a\)的方案数乘以(任选若干个不包括\(x和y\)的字符总出现次数为\(n/2\)的方案数)就是答案

注意到\(xy\)实际上只有\(52*52\)种,可以把它们都处理出来,运用退背包的方法可以\(O(52*52*n)\)处理出答案

一层退背包:(\(dp[i]\)表示不做限制次数和为\(i\)的方案数,\(g[j][i]\)表示不含\(j\)和为\(i\)的方案数)

\[g[j][i] = dp[i]\ \ \ (i < cnt[j])
\]

\[g[j][i] = dp[i] - g[j][i - cnt[j]]\ \ \ (i >= cnt[j])
\]

可以\(O(n*m)\)处理出所有的\(g\), (\(n\)为物品数,\(m\)为背包容量)

实际上用的使用不用再开一个数组\(g\), 从小到大更改\(dp\)就可以了,处理完再倒着还原

AC代码:

#include <bits/stdc++.h>
using namespace std; const int N = 1e5 + 10, mod = 1e9 + 7; long long qp(long long a, long long n, int m = mod) {
long long res = 1;
while(n) {
if(n & 1)
res = res * a % m;
a = a * a % m;
n >>= 1;
}
return res;
} long long fac[N], inv[N], res[55][55], num, dp[N];
int cnt[55], q, x, y, n;
char s[N]; int mp(int x) {
if('a' <= x && x <= 'z')
return x - 'a';
else
return x - 'A' + 26;
} void init() {
fac[1] = 1;
for(int i = 2; i < N; ++i)
fac[i] = fac[i - 1] * i % mod;
inv[N - 1] = qp(fac[N - 1], mod - 2, mod);
for(int i = N - 2; ~i; --i)
inv[i] = inv[i + 1] * (i + 1) % mod;
} int main() {
init();
scanf("%s%d", s, &q);
n = strlen(s);
for(int i = 0; i < n; ++i) {
cnt[mp(s[i])]++;
}
num = fac[n / 2] * fac[n / 2] % mod;
dp[0] = 1;
for(int i = 0; i < 52; ++i) {
if(cnt[i]) {
num = num * inv[cnt[i]] % mod;
for(int j = n; j >= cnt[i]; --j)
dp[j] = (dp[j] + dp[j - cnt[i]]) % mod;
}
}
for(int i = 0; i < 52; ++i) {
res[i][i] = dp[n / 2];
if(cnt[i]) {
for(int j = cnt[i]; j <= n; ++j)
dp[j] = (dp[j] - dp[j - cnt[i]] + mod) % mod;
for(int j = i + 1; j < 52; ++j) {
if(cnt[j]) {
for(int k = cnt[j]; k <= n; ++k) {
dp[k] = (dp[k] - dp[k - cnt[j]] + mod) % mod;
}
res[i][j] = res[j][i] = 2LL * dp[n / 2] % mod;
for(int k = n; k >= cnt[j]; --k) {
dp[k] = (dp[k] + dp[k - cnt[j]] + mod) % mod;
}
}
}
for(int j = n; j >= cnt[i]; --j)
dp[j] = (dp[j] + dp[j - cnt[i]] + mod) % mod;
}
}
while(q--) {
scanf("%d%d", &x, &y);
printf("%lld\n", num * res[mp(s[x - 1])][mp(s[y - 1])] % mod);
}
return 0;
}

最新文章

  1. gulp 安装笔记
  2. Hadoop.2.x_无秘钥设置
  3. [转]有关WorldWind1.4的worldwind.cs窗口设计器打开错误的解决方法
  4. POJ3009 Curling
  5. Drupal 7.31 SQL注入漏洞利用具体解释及EXP
  6. OC 和 swift 小结
  7. C# Generic(转载)
  8. cakephp recursive -1,0,1,2 速查
  9. sql 建立数据库,表格,索引,主键
  10. ASP.NET基础之HttpModule学习
  11. setAction方法 Snackbar 右侧按钮可以被点击并处理一些事件
  12. 【性能测试工具】-SIEGE、HTTP_LOAD、WebBench、Apache-ab
  13. 推荐一个比FiddlerCore好用的HTTP(S)代理服务器
  14. SpringMVC——使用RequestDispatcher.include()和HttpServletResponseWrapper动态获取jsp输出内容
  15. 实现A-Z滑动检索菜单
  16. 《Mysql 用户管理》
  17. 学习Android(入门基础和实用教程)
  18. HTML-XMLHttpRequest
  19. [转载]GBK 汉字内码扩展规范编码表(1.0 版)
  20. More Effective C++ 35个改善方法

热门文章

  1. sonar+Jenkins代码覆盖率检测
  2. Qt在线讲座之QML脚本书写规范
  3. 去除input框的值
  4. 简述C++中的多态机制
  5. windowbuilder安装
  6. Machine Learning in Action(3) 朴素贝叶斯算法
  7. [数据挖掘课程笔记]关联规则挖掘 - Apriori算法
  8. RTSP(Real Time Streaming Protocol)学习笔记 -- RFC2326
  9. ThinkPHP验证码不现实的处理方法
  10. ImageIO 操作图片