Codeforces Round #675 (Div. 2)C. Bargain

前言:切题切到硬石头了,特来记录一下。


题目传送门

C. Bargain
  题目类型:数学,递推。
  解析:在分析题目之前,我们先引入这样的思想:分位求贡献。对于一个数,例如:5037489,它的第七位是9,对整个数的贡献是9*(10^0) = 9第六位是8,对整个数的贡献是8*(10^1) = 80,以此类推。
  下面我们逐步增加难度,对于5037489,我们删除其中的第二位到第五位,也就是374,可以发现374右边的数贡献不会变,而左边的数贡献缩小了10^3倍。
  这样我们就得到了一个规律:对于某一位数,删除其左边一部分,它的贡献不会变;删除其右边k个数,贡献缩小10^k
  题目要求我们求出所有删除段的情况下,剩余数的组合,我们只需要枚举每一位 i,求出删除其左边1 ~ i-1个数的贡献,加删除右边1 ~ n-i 的贡献。
  最后一个问题:如何O(1)求出删右边的贡献?我们从右向左来考虑。5037489,
  对于8,可以保留右边0位,1种方法。
  对于4,可以保留右边0位,1种方法;保留1位,2种方法。
  对于7,可以保留右边0位,1种方法;保留1位,2种方法;保留2位,3种方法。
  可以发现不同位下的数字保留右边相同数量的位数的方法数是一样的。
  那么就可以递推了,设第i位的位数贡献为C[i],则C[i] = C[i-1] + (n-i)*(10^(n-i-1)) 。 第i位的总贡献为a[i]*c[i]。
  **再算一下删左边的就行了,很简单我就不说了。 **

  code:

#include 
#define mod 1000000007
using namespace std;
 
long long n,ans,C = 0,mi10[101010],pre[101010];
char a[101010];
 
int main()
{
     
    ios::sync_with_stdio(false);
    cin >> a+1 ;
    n = strlen(a+1);
    mi10[0] = 1;
    for(int i = 1 ; i <= n ; ++i){
     
        mi10[i] = (mi10[i-1] * 10)%mod;
        pre[i] = (pre[i-1] + i)%mod;
    }
    for(int i = n-1 ; i >= 1 ; --i){
     
        C = (C + mi10[n-i-1]*(n-i)%mod)%mod;
        ans = (ans + ((a[i]-'0')*C)%mod )%mod;
    }
    for(int i = 2 ; i <= n ; ++i){
     
        ans = (ans + pre[i-1]*(a[i]-'0')%mod*mi10[n-i]%mod)%mod;
    }
    cout << ans << endl ;
    return 0;
}

你可能感兴趣的:(算法,codeforce,动态规划,算法,动态规划)