要点
- 优质题解
- 因为只有某type坏人全部分布在同一撇时,才能一次消灭。所以题目安排完毕后一定是type(x)和type(y)占一半,其余占另一半。
- 实际情况只有52*52种,则预处理答案
- 枚举某两种,并连续两次使用退背包得到无排列的方案数,真·答案是有排列的,乘上一个排列数即可,而根据式子,排列数恰好与方案细节无关,是个与(|s|)和全部(cnt[i])有关的定值
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
string s;
int q, len, x, y;
int cnt[55];
ll f[maxn >> 1], g[maxn >> 1], h[maxn >> 1], Ans[55][55];
ll fac[maxn], finv[maxn];
inline int id(char c) {
return c >= 'A' && c <= 'Z' ? c - 'A' + 26 : c - 'a';
}
ll ksm(ll a, int b) {
ll res = 1LL;
for (; b; b >>= 1) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
}
return res;
}
ll pre() {
fac[0] = 1;
for (int i = 1; i <= len; i++) {
fac[i] = fac[i - 1] * i % mod;
}
finv[len] = ksm(fac[len], mod - 2);
for (int i = len - 1; i; --i) {
finv[i] = finv[i + 1] * (i + 1) % mod;
}
ll res = 2LL * fac[len / 2] % mod * fac[len / 2] % mod;
for (int i = 0; i < 52; i++) {
if (!cnt[i]) continue;
res = res * finv[cnt[i]] % mod;
}
return res;
}
int main() {
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> s;
for (auto i : s) {
cnt[id(i)]++;
}
len = s.length();
ll base = pre();
f[0] = 1;
for (int i = 0; i < 52; i++) {
if (!cnt[i]) continue;
for (int j = len / 2; j >= cnt[i]; --j) {
f[j] = (f[j] + f[j - cnt[i]]) % mod;
}
}
for (int i = 0; i < 52; i++) {
if (!cnt[i]) continue;
for (int j = 0; j <= len / 2; j++) {
if (j < cnt[i]) g[j] = f[j];
else g[j] = (f[j] - g[j - cnt[i]] + mod) % mod;
}
Ans[i][i] = base * g[len / 2] % mod;
for (int j = i + 1; j < 52; j++) {
if (!cnt[j]) continue;
for (int k = 0; k <= len / 2; k++) {
if (k < cnt[j]) h[k] = g[k];
else h[k] = (g[k] - h[k - cnt[j]] + mod) % mod;
}
Ans[i][j] = Ans[j][i] = base * h[len / 2] % mod;
}
}
for (cin >> q; q; q--) {
cin >> x >> y;
cout << Ans[id(s[x - 1])][id(s[y - 1])] << '
';
}
return 0;
}