2018年第四階段組隊訓練賽二十四場G區間權值
阿新 • • 發佈:2018-12-16
問題 G: 區間權值
時間限制: 1 Sec 記憶體限制: 128 MB 提交: 82 解決: 39 [提交] [狀態] [討論版] [命題人:admin]
題目描述
小Bo有n個正整數a1..an,以及一個權值序列w1…wn,現在他定義 現在他想知道的值,需要你來幫幫他 你只需要輸出答案對109+7取模後的值
輸入
第一行一個正整數n 第二行n個正整數a1..an 第三行n個正整數w1..wn 1≤n≤3×105 1≤ai≤107 1≤wi≤107
輸出
輸出答案對109+7取模後的值
樣例輸入
3 1 1 1 1 1 1
樣例輸出
10
直接寫肯定是三層for,超時麼誒跑,
因為字首和很熟悉了,所以直接去掉一層,也就是
但是還是兩層的for,O(N^2),這肯定也不行,
先寫個暴力看下,注意輸入的時候,將a[i] = a[i] + a[i-1],就將a陣列直接變成了字首和sum陣列
for(int i=1; i<=n; i++)
for(int j=i; j<=n; j++)
ans1 = (ans1 + (a[j]-a[i-1]+p)%p*w[j-i+1]%p)%p;
以我對這個的理解就把他等價成了下面這個,就是從暴力點變成了暴力區間長度
for(int i=1; i<=n; i++) for(int j=0; j<=n-i; j++) ans2 = (ans2 + w[i]*(a[j+i]-a[j]+p)%p)%p;
那麼明顯可以這樣
提出公因式w[i],然後將中間的求和即可
那麼注意觀察,中間的項
例如
注意中間有些項抵消了,那麼我們需要的就只是前面和後面的某些項就可以了,
例如上面那個就是
顯然又需要字首和來求區間和,那麼用另一個數組存輸入的字首和的字首和就好了
程式碼A了,但是還有些小問題,就是本來應該對稱的,但是不知道為什麼在i小於n/2的時候有些字首和是不對的
所以我一律採用後半段計算的字首和,
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n; const int maxn = 3e5+7,p = 1e9+7; ll a[maxn],b[maxn],w[maxn]; int main() { cin>>n; //while(cin>>n) { for(int i=1; i<=n; i++) { scanf("%lld",&a[i]); a[i] = (a[i] + a[i-1])%p; b[i] = (a[i] + b[i-1])%p; } for(int i=1; i<=n; i++) scanf("%lld",&w[i]); ll ans1 = 0, ans2 = 0,ans3 = 0; /*for(int i=1; i<=n; i++) { for(int j=i; j<=n; j++) { ans1 = (ans1 + (a[j]-a[i-1]+p)%p*w[j-i+1]%p)%p; } } for(int i=1; i<=n; i++) { ll t = 0; for(int j=0; j<=n-i; j++) { ll c = w[i]*(a[j+i]-a[j]+p)%p; ans2 = (ans2 + c)%p; t = (t+c)%p; //if(i==3) //printf("%d->%d----%lld\n",j,j+i,c); } printf("%d---%lld\n",i,t/w[i]); }*/ for(int i=1; i<=n; i++) { int ti = i<=n/2?n-i+1:i; ll t = ((b[n] - b[n/ti*ti-1]+p)%p - b[n%ti] +p )%p*w[i]%p; //printf("%d--%lld\n",i,t/w[i]); ans3 = (ans3 + t)%p; } printf("%lld\n",ans3); } return 0; } /* 7 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1->3----18 2->4----27 3->5----36 4->6----45 5->7----54 1--28 2--96 3--144 4--256 5--300 6--288 7--196 1344 1344 1308 */ /* 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1---36 2---126 3---243 4---360 5---450 6---486 7---441 8---288 1--36 2--72 3--243 4--144 5--450 6--486 7--441 8--288 2430 2430 2160 */