codeforces1111d(dp、组合数学)

本文深入解析CodeForces上的一道难题D.DestroytheColony,通过dp+组合数学的方法解决1e5次询问,涉及字符串操作、组合计数及算法优化。详细介绍了如何在给定条件下计算可能的字符串排列,利用逆元、阶乘和组合数加速计算。

D. Destroy the Colony

time limit per test

2 seconds

memory limit per test

512 megabytes

input

standard input

output

standard output

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 ii-th character of the string represents the type of villain in the ii-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 qq questions. In each question, he gives Jarvis two numbers xx and yy. 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 xx-th hole or yy-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 ss (2≤|s|≤1052≤|s|≤105), representing the initial colony arrangement. String ss can have both lowercase and uppercase English letters and its length is even.

The second line contains a single integer qq (1≤q≤1051≤q≤105) — the number of questions.

The ii-th of the next qq lines contains two integers xixi and yiyi (1≤xi,yi≤|s|1≤xi,yi≤|s|, xi≠yixi≠yi) — the two numbers given to the Jarvis for the ii-th question.

Output

For each question output the number of arrangements possible modulo 109+7109+7.

Examples

input

Copy

abba
2
1 4
1 2

output

Copy

2
0

input

Copy

AAaa
2
1 2
1 3

output

Copy

2
0

input

Copy

abcd
1
1 3

output

Copy

8

Note

Consider the first example. For the first question, the possible arrangements are "aabb" and "bbaa", and for the second question, index 11 contains 'a' and index 22 contains 'b' and there is no valid arrangement in which all 'a' and 'b' are in the same half.

题目链接:http://codeforces.com/problemset/problem/1111/D

题意:一串包含大小写字母的串(长度为偶数),现在有q次询问,每次给出x,y表示串中对应位置的字符(假设为a、b),现在要求所有a、b同在串的前半段或者后半段,其余相同的字符要在串前半段或者后半段。

思路:dp+组合数学。

虽然询问次数为1e5,但是实际上选出来两个字符的种类数只有52X52种(注意到这点就比较好做了)。首先在len/2中选出位置放置两种字符,然后再安排其余的字符。我们可以dp出由这些字符来组成的所有长度的方案数(不考虑顺序,相当于集合)即01背包,然后再面对不同的a、b时只需要在原基础上减去这两个字符的情况就可以。我们可以求出没有a、b的组成长度为len/2的方案数。现在这些是集合状态,要把他们化成有顺序的串用到排列公式。注意到所有的方案中其排列的种数还是一样的,因为分母一样、分子也一样,这点很容易的得到。

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+100;
const ll mod = 1e9+7;
char s[maxn];  //input string
int num[55];   //element cnt
ll dp[maxn];
ll tmp[maxn];
ll quesnum[55][55];
ll ques[55][55];
ll inv[maxn],fac[maxn],invf[maxn];   //inv[i]为i的逆元,fac[i]为阶乘i!,invf[i]为阶乘i!的逆元
void init(int hf)
{
    inv[1]=1;  
    for (int i=2;i<maxn;i++)  
        inv[i]=((mod-mod/i)*inv[mod%i])%mod; 
    fac[0] = 1; 
    for(int i = 1;i<maxn;i++)
        fac[i] = (fac[i-1] * (ll)i)%mod;
    invf[0] = 1;
    for(int i = 1;i<maxn;i++)
        invf[i] = (invf[i-1]*inv[i])%mod;
    
    memset(dp,0,sizeof(dp));
    memset(ques,-1,sizeof(ques));
    dp[0] = 1;
    for(int i =0;i<55;i++)
    {
        if(!num[i])continue;
        for(int j = hf;j>=num[i];j--)
        {
            dp[j] = (dp[j] + dp[j - num[i]])%mod;
        }
    }
    for(int i = 0;i<52;i++)
    {
        for(int j = i;j<52;j++)
        {
            for(int k = 0;k<=hf;k++)tmp[k] = dp[k];
            for(int k = num[i];k<=hf;k++)tmp[k] = (tmp[k] - tmp[k-num[i]] + mod)%mod;
            if(i!=j)
                for(int k = num[j];k<=hf;k++)tmp[k] = (tmp[k] - tmp[k-num[j]] + mod)%mod;
            quesnum[i][j] = quesnum[j][i] = tmp[hf];
        }
    }
}
int main()
{
    
    scanf("%s",s);
    memset(num,0,sizeof(num));
    int len = strlen(s);
    int hf = len/2;
    for(int i = 0;i<len;i++)
    {
        if(s[i]>='a'&&s[i]<='z')num[s[i] - 'a']++;
        else num[s[i] - 'A' + 26]++;
    }
    init(hf);
    int q;
    scanf("%d",&q);
    int x,y;
    while(q--)
    {
        scanf("%d%d",&x,&y);
        x--;y--;
        int idx,idy;
        if(s[x]>='a'&&s[x]<='z')idx = s[x] - 'a';
        else idx = s[x] - 'A' + 26;
        if(s[y]>='a'&&s[y]<='z')idy = s[y] - 'a';
        else idy = s[y] - 'A' + 26;
        if(ques[idx][idy]!=-1)
        {
            printf("%I64d\n",ques[idx][idy]);
            continue;
        }
        ll ans;
        if(idx == idy)
        {
            if(num[idx]>hf)
            {
                printf("0\n");
                ques[idx][idx] = 0;
                continue;
            }
            ll cc = ((fac[hf] * invf[num[idx]] )%mod* invf[hf - num[idx]])%mod;
            ans = ((cc * fac[hf - num[idx]])%mod * fac[hf])%mod;
        }
        else
        {
            if(num[idx] + num[idy]>hf)
            {
                ques[idx][idy] = ques[idy][idx] = 0;
                printf("0\n");
                continue;
            }
            int numx = num[idx],numy = num[idy];
            int num_hfx = hf - numx;
            ll cc1 = ((fac[hf] * invf[numx] )%mod* invf[hf - numx])%mod;
            ll cc2 = ((fac[num_hfx] * invf[numy] )%mod* invf[num_hfx - numy])%mod;
            ans = (((cc1 * cc2)%mod * fac[hf])%mod * fac[hf - numx - numy])%mod;
        }
        for(int i = 0;i<55;i++)
        {
            if(i==idx || i == idy)continue;
            ans = (ans * invf[num[i]])%mod;
        }
        ans = (ans * quesnum[idx][idy])%mod;
        ans = (ans * 2)%mod;
        ques[idx][idy] = ques[idy][idx] = ans;
        printf("%I64d\n",ans);
    }
}

 

Codeforces Gym 101630 是一场编程竞赛,通常包含多个算法挑战问题。这些问题往往涉及数据结构、算法设计、数学建模等多个方面,旨在测试参赛者的编程能力和解决问题的能力。 以下是一些可能出现在 Codeforces Gym 101630 中的题目类型及解决方案概述: ### 题目类型 1. **动态规划(DP)** 动态规划是编程竞赛中常见的题型之一。问题通常要求找到某种最优解,例如最小路径和、最长递增子序列等。解决这类问题的关键在于状态定义和转移方程的设计[^1]。 2. **图论** 图论问题包括最短路径、最小生成树、网络流等。例如,Dijkstra 算法用于求解单源最短路径问题,而 Kruskal 或 Prim 算法则常用于最小生成树问题[^1]。 3. **字符串处理** 字符串问题可能涉及模式匹配、后缀数组、自动机等高级技巧。KMP 算法和 Trie 树是解决此类问题的常用工具[^1]。 4. **数论与组合数学** 这类问题通常需要对质数、模运算、排列组合等有深入的理解。例如,快速幂算法可以用来高效计算大数的模幂运算[^1]。 5. **几何** 几何问题可能涉及点、线、多边形的计算,如判断点是否在多边形内部、计算两个圆的交点等。向量运算和坐标变换是解决几何问题的基础[^1]。 ### 解决方案示例 #### 示例问题:动态规划 - 最长递增子序列 ```python def longest_increasing_subsequence(nums): if not nums: return 0 dp = [1] * len(nums) for i in range(len(nums)): for j in range(i): if nums[i] > nums[j]: dp[i] = max(dp[i], dp[j] + 1) return max(dp) # 示例输入 nums = [10, 9, 2, 5, 3, 7, 101, 18] print(longest_increasing_subsequence(nums)) # 输出: 4 ``` #### 示例问题:图论 - Dijkstra 算法 ```python import heapq def dijkstra(graph, start): distances = {node: float('infinity') for node in graph} distances[start] = 0 priority_queue = [(0, start)] while priority_queue: current_distance, current_node = heapq.heappop(priority_queue) if current_distance > distances[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances # 示例输入 graph = { 'A': {'B': 1, 'C': 4}, 'B': {'A': 1, 'C': 2, 'D': 5}, 'C': {'A': 4, 'B': 2, 'D': 1}, 'D': {'B': 5, 'C': 1} } start = 'A' print(dijkstra(graph, start)) # 输出: {'A': 0, 'B': 1, 'C': 3, 'D': 4} ``` #### 示例问题:字符串处理 - KMP 算法 ```python def kmp_failure_function(pattern): m = len(pattern) lps = [0] * m length = 0 # length of the previous longest prefix suffix i = 1 while i < m: if pattern[i] == pattern[length]: length += 1 lps[i] = length i += 1 else: if length != 0: length = lps[length - 1] else: lps[i] = 0 i += 1 return lps def kmp_search(text, pattern): n = len(text) m = len(pattern) lps = kmp_failure_function(pattern) i = 0 # index for text j = 0 # index for pattern while i < n: if pattern[j] == text[i]: i += 1 j += 1 if j == m: print("Pattern found at index", i - j) j = lps[j - 1] elif i < n and pattern[j] != text[i]: if j != 0: j = lps[j - 1] else: i += 1 # 示例输入 text = "ABABDABACDABABCABAB" pattern = "ABABCABAB" kmp_search(text, pattern) # 输出: Pattern found at index 10 ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值