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\)
這種數有 \(\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; }