CodeForces 1422 C Bargain 题解
题意
给你一个数 \(a\)(长度为 \(|a|\) ) ,你可以从这个数中选择一段子串并删掉,两端再拼成一个新数,问所有可能的新数的和\(\mod 10^9+7\)
\(\texttt{Data Range:}\)
\(|a| <= 10^5\)
变量声明
\(a = a_1a_2a_3\dots a_n\)
\(n\) : \(|a|\)
思路
\(|a| <= 10^5\)
看一下范围,暴力是不可能的了
那就要分别考虑每个数位对答案的贡献。
假设当前数位为第 \(k\) 位,
那么删去的串一定在第 \(k\) 位前或者后面。
在第 k 位前
产生的新数中,\(a_k\) 后面一定有 \(n-k\) 位,对答案的贡献为 \(10^{n-k} * a_k\)
这种数有 \(\dbinom{k}{2}\) 个
总贡献为 \(\dbinom {k}{2} * 10^{n-k} * a_k\)
在第 k 位后
产生的新数中,\(a_k\) 后面的位数与在后面删去的个数有关.
若在后面删去 \(x(x \leq n-k)\) 位, 这种数对答案的贡献为 \(10^{n-k-x} * a_k\) ,
共有 \(n-k-x+1\) 种。
\(\therefore\) 对答案的贡献为 \(10^{n-k-x} * a_k *(n-k-x+1)\)
这种情况总的对答案贡献为
\(\sum\limits_{x=1}^{n-k} 10^{n-k-x} * a_k *(n-k-x+1)\)
直接这么算,肯定要超时了,我们注意到:
\(10^{n-k-x}\)
\(n-k-x+1\)
也就是说,对于每项,都可以表示为 \(10^t * (t+1) * a_k\) 。
而只有 \(a_k\) 是不确定的,
因此,我们维护一个数组,只记录各项 \(a_k\) 的和,到时候再乘上 \(10^{n-k-x} *(n-k-x+1)\) 就可以了。
又因为 \(x \in [1,n-k]\) ,每次处理 \(a_k\) 都是一次区间加,而最后计算结果时是一位一位的求值,维护一个差分数组就行了。
代码
头文件、快读等自由脑补
#define ll long long
using namespace std;
const int MAXN = 1e5+10;
const int mod = 1e9+7;
int n = 0;
ll a[MAXN],c[MAXN],p10[MAXN],ans = 0;
int main (){
while(scanf("%1lld",&a[n+1]) == 1) n++;
p10[0] = 1;
for(int i = 1;i <= n;i++) p10[i] = p10[i-1] * 10 % mod;
//p10[i] 10^i
for(ll i = 1;i <= n;i++){
ans += (i*(i-1)/2)%mod * p10[n-i] %mod* a[i]%mod;
if(ans >= mod) ans %= mod;
//在第i位前
c[n-i+1] = (c[n-i+1]-a[i])%mod;
c[1] = (c[1]+a[i])%mod;
//根据推导,对数组进行区间加(实际上是对差分数组修改)
}
ll te = 0;
for(int i = 1;i <= n;i++){
te = (te+c[i])%mod; //还原数组
ans = (ans + te * p10[i-1] % mod * i%mod)%mod; //计算第二部分答案
}
printf("%lld",ans%mod);
return 0;
}