【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;
}
原文地址:https://www.cnblogs.com/qswx/p/9155674.html