P2671 求和 (NOIP 2015)

传送门

分析

  • 40 pts : \(O(n^2)\)

    将原先的 \(y-x = z-y\) 化为 \(2\times y = x + z\) ,只要暴力枚举 \(x,z\) 即可

  • 100 pts: \(O(n)\)

    通过 40 分的做法,我们可以发现,对于一个符合要求的 \(y\) ,必须要 \(x,z\) 保持同号

    那么我们不妨可以假设存在集合 \(A\) ,是得该集合内的所有元素(方格)为同种颜色,且符号相同的

    于是,我们可以整理出这么一个式子:对于集合内的元素 \(x_i\) ,有 \((x_1 + x_2) * (num_1+num_2) + (x_1+x_3)*(num_1+num_3)+ …… +(x_1+x_n)*(num_1+num_n)+……+(x_{n-1}+x_n)*(num_{n-1}+num_{n})\)

    对于这个毫无规律的式子,我们可以先研究它的所有包含 \(x_1\) 的项,将其全部提出,可得 \(i_1*num_1 + i_1*num_2 +i_1*num_1+i_1*num_3+……+i_1*num_1+i_1*num_n\)

    进一步合并为 \(i_1*num_1*(n-1)+i_1*(num_2+num_3+num_4+……+num_n)\)

    那么:我们可以归纳出这样一个式子:假设该种集合中共有 \(k\) 项,对于第 \(n\) 项,有 \(i_n*num_n*(k-1)+i_n*(num_1+num_2+……+num_{n-1}+num_{n+1}+……+num_{k})\)

    对于这一串式子,可以先将其加上一个 \(i_n*num_n\),再减去一个 \(i_n*num_n\)

    即为:\(i_n*num_n*(k-2)+i_n*(num_1+num_2+……+num_{n-1}+num_n+num_{n+1}+……+num_{k})\)

    对于 \((num_1+num_2+……+num_{n-1}+num_n+num_{n+1}+……+num_{k})\),我们可以直接前缀和处理得到


源代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
#define ll long long
using namespace std;

const ll mod=10007;
const ll maxn=1e5+10;
ll n,m,ans;
ll sum1[maxn][2];
ll sum2[maxn][2];
struct node
{
	ll num,c;
} s[maxn];

int main(void)
{
	scanf("%lld%lld",&n,&m);
	
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&s[i].num);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&s[i].c);
		sum1[s[i].c][i%2]++;
		(sum2[s[i].c][i%2]+=s[i].num)%=mod;
	}
	
	for(int i=1;i<=n;i++)
	{
		(ans+=(i*((sum1[s[i].c][i%2]-2)*s[i].num+sum2[s[i].c][i%2])))%=mod;
	}
	
	printf("%lld\n",ans);
	
	return 0;
}
posted @ 2020-09-05 21:32  雾隐  阅读(141)  评论(0编辑  收藏  举报