洛谷 P2671 求和
題目描述
一條狹長的紙帶被均勻劃分出了nn個格子,格子編號從11到nn。每個格子上都染了一種顏色color\_icolor_i用[1,m][1,m]當中的一個整數表示),並且寫了一個數字number\_inumber_i。
定義一種特殊的三元組:(x,y,z)(x,y,z),其中x,y,zx,y,z都代表紙帶上格子的編號,這裡的三元組要求滿足以下兩個條件:
-
x,y,z是整數,x<y<z,y-x=z-y
-
colorx=colorz
滿足上述條件的三元組的分數規定為)(x+z)×(number_x+number_z)。整個紙帶的分數規定為所有滿足條件的三元組的分數的和。這個分數可能會很大,你只要輸出整個紙帶的分數除以10,007所得的餘數即可。
輸入格式:
第一行是用一個空格隔開的兩個正整數nn和m,nm,n表紙帶上格子的個數,mm表紙帶上顏色的種類數。
第二行有nn用空格隔開的正整數,第ii數字numbernumber表紙帶上編號為ii格子上面寫的數字。
第三行有nn用空格隔開的正整數,第ii數字colorcolor表紙帶上編號為ii格子染的顏色。
輸出格式:
一個整數,表示所求的紙帶分數除以1000710007所得的餘數。
思路分析
直接暴力肯定是不行的,我們需要把各種相同的顏色分離出來計算。
但是隻分離出來沒有用,因為三元組要求中間有一個y。可以看出,x和z的奇偶性相同時,存在y使三元組成立。
所以我們用兩個陣列儲存相同顏色的奇、偶情況,每次計算就變成計算這個序列的值。
則原式=(num[1]+num[2])*(a[num[1]]+a[num[2])+(num[1]+num[3])*(a[num[1]]+a[num[3])+(num[1]+num[4])*(a[num[1]]+a[num[4])+
...+(num[2]+num[3])*(a[num[2]]+a[num[3])+(num[2]+num[4])*(a[num[2]]+a[num[4])+
...+(num[n-1]+num[n])*(a[num[n-1]]+a[num[n])
其中,num[1]*a[num[1]]這個值被計算了n-1次,num[1]乘其他a[num[2]],a[num[3]],a[num[4]]……都為一次。
num[2]*a[num[2]]這個值被計算了n-2次,num[1]乘其他a[num[1]],a[num[3]],a[num[4]]……都為一次。
以此類推。
程式碼
#include<iostream> #include<vector> #include<cmath> using namespace std; long long n,m,a[100001],ans=0; //資料比較大,用longlong vector<long long> even[100001],odd[100001]; //使用變長陣列儲存 int ad(int pt) { long long count=0,ep=even[pt].size(),op=odd[pt].size(),s=0; for(int i=1;i<=ep;i++) { s=(s+a[even[pt][i-1]])%10007;//所有值的總和 } for(int i=1;i<=ep;i++) { count+=(s%10007+(ep-2)*(a[even[pt][i-1]]%10007))%10007*even[pt][i-1]%10007; count%=10007; //一定要邊/邊%! } s=0; for(int i=1;i<=op;i++) { s=(s+a[odd[pt][i-1]])%10007; } for(int i=1;i<=op;i++) { count+=(s%10007+(op-2)*(a[odd[pt][i-1]]%10007))%10007*odd[pt][i-1]%10007; count%=10007; } return count; } int main() { long long cl; cin>>n>>m; for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=1;i<=n;i++) { cin>>cl; if(i&1) odd[cl].push_back(i); else even[cl].push_back(i); } for(int i=1;i<=m;i++) { ans+=ad(i); ans%=10007; } cout<<ans; }