【Solve summary】求和
【Problem description】
問題描述
一條狹長的紙帶被均勻劃分出了n個格子,格子編號從1到n。每個格子上都染了一種顏色color_i(用[1,m]當中的一個整數表示),並且寫了一個數字number_i。
定義一種特殊的三元組:(x,y,z),其中x,y,z都代表紙帶上格子的編號,這裏的三元組要求滿足以下兩個條件:
xyz是整數,x<y<z,y-x=z-y colorx=colorz
滿足上述條件的三元組的分數規定為(x+z)*(number_x+number_z)。整個紙帶的分數規定為所有滿足條件的三元組的分數的和。這個分數可能會很大,你只要輸出整個紙帶的分數除以10,007所得的余數即可。
【Input format】
第一行是用一個空格隔開的兩個正整數n和m,n表紙帶上格子的個數,m表紙帶上顏色的種類數。
第二行有n用空格隔開的正整數,第i數字number表紙帶上編號為i格子上面寫的數字。
第三行有n用空格隔開的正整數,第i數字color表紙帶上編號為i格子染的顏色。
【Output format】
共一行,一個整數,表示所求的紙帶分數除以10,007所得的余數。
【Algorithm design】
Simple enumeration + Mathematics deal
【Problem analysis】
Consequent不可取
Mathematics思維
核心:數據分離
N太大 直接搞O(N2)
讀題
x<y<z,y-x=z-y,colorx=colorz
翻譯一下
x,z奇偶性相同且同種顏色
那麽把n拆成兩條鏈
奇數鏈
偶數鏈
兩鏈互不相幹
對於鏈上所有同種顏色的點 都可以求分數
既然如此再看
(x+z)*(number_x+number_z)
拆解後
x*num_x+z*num_z+x*num_z+z*num_x
前綴和思想
取一點x 那麽關於x的所有三元組的分數之和
即
x*num_x*cnt_others + sumcnt_others + x*numcnt_others + num_x*idcnt_others
所以只需要分兩條鏈
枚舉每個點
根據其顏色進行處理
O(2n)
Anyway
把cnt處理完了再搞會有一倍冗出
所以邊枚舉邊搞最好
先前忘了這點楞了幾分鐘
錯誤記錄
沒把ans求余
沒考慮sum也很大也要求余
即使執行上述操作 sum還是很大
LL型適配
【Source code】
#include <bits/stdc++.h>
#define F(i,j,k) for(int i=j;i<=k;i++)
#define D(i,j,k) for(int i=j;i>=k;i--)
#define sc(i) scanf("%lld",&i)
#define mo 10007
#define ll long long
#define R return
using namespace std;
int n,m;
ll ans,num[100010],col[100010],sumnu[100010],sum[100010],sumid[100010],cnt[100010];
void read()
{
cin>>n>>m;
F(i,1,n)sc(num[i]);
F(i,1,n)sc(col[i]);
R;
}
void work()
{
F(jff,1,2)
{
memset(sumid,0,sizeof(sumid));
memset(sumnu,0,sizeof(sumnu));
memset(sum,0,sizeof(sum));
memset(cnt,0,sizeof(cnt));
for(int i=jff;i<=n;i+=2)
{
ans=(ans+sum[col[i]]+num[i]*i*cnt[col[i]])%mo;
ans=(ans+sumnu[col[i]]*i)%mo;
ans=(ans+sumid[col[i]]*num[i])%mo;
sumid[col[i]]=(sumid[col[i]]+i)%mo;
sumnu[col[i]]=(sumnu[col[i]]+num[i])%mo;
sum[col[i]]=(sum[col[i]]+num[i]*i)%mo;
cnt[col[i]]++;
}
}
cout<<ans<<endl;
R;
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
read();
work();
R 0;
}
【Solve summary】求和