Codeforces Contest 1111 problem D Destroy the Colony —— DP+组合数学,求将一个区间分成两个,不改变两个区间中的数的情况数

202 篇文章 6 订阅
91 篇文章 0 订阅

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≤|s|≤105), 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≤q≤105) — the number of questions.

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

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

Examples
inputCopy
abba
2
1 4
1 2
outputCopy
2
0
inputCopy
AAaa
2
1 2
1 3
outputCopy
2
0
inputCopy
abcd
1
1 3
outputCopy
8
Note
Consider the first example. For the first question, the possible arrangements are “aabb” and “bbaa”, and for the second question, index 1 contains ‘a’ and index 2 contains ‘b’ and there is no valid arrangement in which all ‘a’ and ‘b’ are in the same half.

题意:

给你长度为n的串,每次给你两个点的位置,输出将这两个位置的值的所有相同的值都放到区间的前半部或者后半部,并且其他相同的值都要在相同的部分的方法数。

题解:

数学本来就不擅长,更何况和dp搞在一起,花了一点时间去理解别人的代码。
首先一看到这道题目肯定是预处理,1e5和1e5的数量O(n)都过不了。然后它只有大写和小写两种字符,所以是52个,然后它每次指定两个字符,所以预处理是52*52*?的时间复杂度。首先需要知道一个东西:将一个区间分成大小一样的两部分,并且每一块的数都不变,所有的情况数是 2 ∗ ( n / 2 ) ! ∗ ( n / 2 ) ! / ( c n t 1 ! + c n t 2 ! + . . + c n t n ! ) 2*(n/2)!*(n/2)!/(cnt1!+cnt2!+..+cntn!) 2(n/2)!(n/2)!/(cnt1!+cnt2!+..+cntn!)
也就是说确定了那些数在同一个区间的所有情况。举个例子:
a b c d 这4个数,如果将ab分在一起,cd分在一起,他们的情况数是ab cd,ab dc,ba cd,ba dc四种,再翻转ab cd,就是8种通过上面的算式算下来也是8种,所以这个算式是确定了哪些字符在同一个块中所有情况数的系数,然后就是找x,y在一个块中,还有哪些可以在这个块中的情况,那么方案数的dp方程就是dp[j]+=dp[j-cnt[i]]。
之后还要for一遍cnt[i]到len/2就是要把长度为cnt[i]的字符放进去,剩下的有多少种可能的情况。答案就是情况数*每种情况的所有排列。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
const ll mod=1e9+7;
ll dp[N],f[N],inv[N],fac[N],ans[55][55];
int change(char s)
{
    return 'a'<=s?s-'a':s-'A'+26;
}
ll qpow(ll a,ll b)
{
    ll ret=a,ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*ret%mod;
        ret=ret*ret%mod;
        b>>=1;
    }
    return ans;
}
char s[N];
int cnt[100];
int main()
{
    int n;
    scanf("%s%d",s+1,&n);
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
        cnt[change(s[i])]++;
    fac[1]=1;
    for(ll i=2;i<N;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[N-1]=qpow(fac[N-1],mod-2);
    for(ll i=N-2;~i;i--)
        inv[i]=inv[i+1]*(i+1)%mod;
    ll d=2ll*fac[len/2]*fac[len/2]%mod;
    for(int i=0;i<=51;i++)
        d=d*inv[cnt[i]]%mod;
    dp[0]=1;
    for(int i=0;i<=51;i++)
    {
        if(!cnt[i])
            continue;
        for(int j=len/2;j>=cnt[i];j--)
            (dp[j]+=dp[j-cnt[i]])%=mod;
    }
    for(int i=0;i<=51;i++)
    {
        for(int j=0;j<=51;j++)
        {
            if(!cnt[j]||!cnt[i])
                continue;
            for(int k=0;k<=len/2;k++)
                f[k]=dp[k];
            for(int k=cnt[i];k<=len/2;k++)
                f[k]=(f[k]-f[k-cnt[i]]+mod)%mod;
            if(i!=j)
                for(int k=cnt[j];k<=len/2;k++)
                    f[k]=(f[k]-f[k-cnt[j]]+mod)%mod;
            ans[i][j]=f[len/2]*d%mod;
        }
    }
    int l,r;
    while(n--)
    {
        scanf("%d%d",&l,&r);
        printf("%lld\n",ans[change(s[l])][change(s[r])]);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值