CSU 1810 Reverse (组合计数)

来源:互联网 发布:阿根廷04男篮名单 数据 编辑:程序博客网 时间:2024/06/06 15:51

1810: Reverse

      Time Limit: 5 Sec     Memory Limit: 128 Mb     Submitted: 189     Solved: 90    


Description

Bobo has a n digits decimal number D=d1 d2…dn (It may have leading zeros).
Let R(i,j) denotes number D with digits between the i-th position and j-th position reversed. That is, R(i,j)=d1…di-1 dj dj-1…di dj+1 dj+2…dn.
Bobo would like to find
modulo (109+7).

Input

The input contains at most 30 sets. For each set:
The first line contains an integer n (1≤n≤105).
The second line contains n digits d1 d2…dn (0≤di≤9).

Output

For each set, an integer denotes the result.

Sample Input

2123012100123456789

Sample Output

45369733424314

Hint

Source

湖南省第十二届大学生计算机程序设计竞赛



         计数的题目总是受欢迎。

         这题题意是可以每次反转一个区间,得到一个数字,然后问你把所有这样不重复的反转之后的数字加起来,结果是多少。

         这种题目通常都是计算贡献,但是计算贡献也有好几种方式,这里就是计算每一个位置对最后结果的贡献。注意到,交换两个区间,实际上可能对某些位置不产生影响,所以说我们分成两种情况,一是这个位置还是原本的数字,二是这个位置是别的数字。当这个位置是原本的数字的时候,相当于只需要计算有多少个这样不影响该位置的区间。这个又可以分为两种情况,意识反转区间根本不包含这个位置,显然有i*(i-1)/2+(n-i)*(n-i+1)/2种;另一种是区间包含这个位置,但是这里是对称轴,显然有min(i,n-i+1)中。

         然后就计算这个位置不是原本的数字的情况。我们以左边为例子说明,假如说第i个位置反转之后是原本的第j个数字,可以看出这个的条件就是对称轴固定在i与j之间,那么满足条件的区间就有min(j,n-i+1)个,这个自己画画就能理解。知道了这个我们就可以再仔细看看这个怎么计算,由于有min(j,n-i+1),所以还是一样分两种情况,当j大的时候这个贡献就是(n-i+1)*a[j],而当j小的时候贡献就是j*a[j]。可以看到,对于一个固定的i,有一部分的贡献是对应j*a[j]的和,一部分是(n-i+1)*a[j],1<=j<=i。那么当i<=n-i+1的时候,贡献直接就是j*a[j]的前缀和。反之,如果i>n-i+1,那就要分成两部分分别相加计算。

         计算完每一位的贡献之后,先把当前ans乘以10,然后在把当前位置的值加上去。具体见代码:

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<vector>#include<cmath>#define mod 1000000007#define LL long long#define N 100010using namespace std;LL a[N],sl[N],sr[N],SL[N],SR[N];char ch[N]; int n;int main(){    while(~scanf("%d%s",&n,ch))    {        sr[n+1]=SR[n+1]=0;        for(int i=0;i<n;i++) a[i+1]=ch[i]-48;        for(int i=1;i<=n;i++)        {            sl[i]=(sl[i-1]+a[i])%mod;            SL[i]=(SL[i-1]+(LL)a[i]*i)%mod;            sr[n-i+1]=(sr[n-i+2]+a[n-i+1])%mod;            SR[n-i+1]=(SR[n-i+2]+a[n-i+1]*i)%mod;        }        LL ans=0,res;        for(int i=1;i<=n;i++)        {            res=a[i]*((LL)i*(i-1)/2+(LL)(n-i)*(n-i+1)/2)%mod;            res+=(LL)a[i]*min(i,n-i+1)%mod;            if (i-1>=n-i+1)            {                res+=SL[n-i+1];                res+=(LL)(sl[i-1]-sl[n-i+1])*(n-i+1)%mod;            } else res+=SL[i-1];            if (n-i>=i)            {                res+=SR[n-i+1];                res+=(LL)(sr[i+1]-sr[n-i+1])*i%mod;            } else res+=SR[i+1];            ans=(ans*10%mod+res)%mod;        }        printf("%lld\n",ans);    }    return 0;}