NOIP2015 普及组的 T3,六年前在师大附中比赛第一次做,当时写了三重循环的暴力,重温一下。
题目描述
一条狭长的纸带被均匀划分出了 (n) 个格子,格子编号从 (1) 到 (n)。
每个格子上都染了一种颜色 (color\_i) 用 ([1,m]) 当中的一个整数表示),并且写了一个数字 (number\_i) 。
定义一种特殊的三元组:((x,y,z)),其中 (x,y,z) 都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:
(xyz) 是整数, (x<y<z,;;y-x=z-y,;;color_x=color\_z)
满足上述条件的三元组的分数规定为 ((x+z) imes (number\_x+number\_z))。
整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以 (10,007) 所得的余数即可。
输入格式
第一行是用一个空格隔开的两个正整数 (n) 和 (m),(n) 表纸带上格子的个数,(m) 表纸带上颜色的种类数。
第二行有 (n) 用空格隔开的正整数,第 (i) 数字 (number) 表纸带上编号为 (i) 格子上面写的数字。
第三行有 (n) 用空格隔开的正整数,第 (i) 数字 (color) 表纸带上编号为 (i) 格子染的颜色。
输出格式
一个整数,表示所求的纸带分数除以 (10007) 所得的余数。
输入 #1
6 2
5 5 3 2 2 2
2 2 1 1 2 1
输出 #1
82
输入 #2
15 4
5 10 8 2 2 2 9 9 7 7 5 6 4 2 4
2 2 3 3 4 3 3 2 4 4 4 4 1 1 1
输出 #2
1388
说明/提示
【输入输出样例 1 说明】
纸带如题目描述中的图所示。
所有满足条件的三元组为: ((1,3,5),(4,5,6)) 。
所以纸带的分数为 ((1 + 5) imes (5 + 2) + (4 + 6) imes (2 + 2) = 42 + 40 = 82) 。
对于第 (1) 组至第 (2) 组数据, (1 ≤ n ≤ 100, 1 ≤ m ≤ 5);
对于第 (3) 组至第 (5) 组数据, (1 ≤ n ≤ 3000, 1 ≤ m ≤ 100);
对于第 (5) 组至第 (6) 组数据, (1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000),且不存在出现次数超过 (2020) 的颜色;
对于全部 (10) 组 数 据 , (1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000, 1 ≤ color\_i ≤ m,1≤number\_i≤100000)。
——————————————————————————————————————————
解题思路
就不说部分分的做法了。
可以注意到给定条件 (x<y<z,;;y-x=z-y) 出现了 (y),但是分数公式却没有出现 (y),
所以 (y) 的唯一条件就是为整数,即 (x) 和 (z) 同为奇数或同为偶数即可进行组合成三元组
为了同时满足 (color\_x=color\_y) 的条件,我们可以按总颜色数 (m) 以及序号的奇偶性分为 (m*2) 个组,每组都是互不影响的
(因为两个方块才要同时满足颜色一致和奇偶性一致条件才能组成三元组)
现在考虑对于每个组如何计算分数,我们只关心方块的序号和数字,假设有某组有三个方块
序号分别为 (x0,;x1,;x2;),数字分别为 (y0,;y1,;y2;),那么这个组的贡献是:
((x0+x1)(y0+y1)+(x0+x2)(y0+y2)+(x1+x2)(y1+y2))
展开后:(x0(2y0+y1+y2)+x1(y0+2y1+y2)+x2(y0+y1+2y2))
如果再加一个方块,就变成了:(x0(3y0+y1+y2+y3)+x1(y0+3y1+y2+y3)+x2(y0+y1+3y2+y3)+x3(y0+y1+y2+3y3))
所以可以先统计 (sum yi),然后遍历每一项,用 (sum yi) 加上 ((t-1)*yi) 后再乘以 (xi) 并求和( (t) 为组中方块数量),就是这个组的贡献
记得要每一步都要模好,开 long long,理论时间复杂度 (O(n))
代码如下
#include <bits/stdc++.h>
#define ll long long
#define MAXN 100007
#define mod 10007
using namespace std;
struct node { ll x,y; };
vector<node> p[MAXN*2];
// 用一个不定数组分奇偶性地存储不同颜色方框的信息
// 例如,颜色为 i 且序号为奇数的方块,插入在 p[i*2-1] 中
// 颜色为 i 且序号为偶数的方块,插入在 p[i*2] 中,类似链式二叉树存储
ll n,m,num[MAXN],ans;
inline ll readint() {
ll X=0,w=0; char ch=0;
while (!isdigit(ch)) w|=ch=='-',ch=getchar();
while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
int main() {
n=readint(),m=readint();
for (ll i=1;i<=n;i++) num[i]=readint();
for (ll i=1;i<=n;i++) {
ll co=readint(); // 按照既定规则存储方块信息
if (i&1) p[(co<<1)-1].push_back((node){i,num[i]});
else p[co<<1].push_back((node){i,num[i]});
}
for (ll i=1;i<2*MAXN;i++) { // 按颜色进行遍历
if ((int)p[i].size()<=1) continue;
ll base=0,tot=(ll)p[i].size();
for (ll j=0;j<tot;j++) base=(base+p[i][j].y)%mod; // base 是 number 的求和
for (ll j=0;j<tot;j++) { // 按推导公式计入答案中,记得每一步都要模住
ll tmp=(base+((tot-2)*p[i][j].y)%mod)%mod;
ans=(ans+p[i][j].x*tmp%mod)%mod;
}
}
printf("%lld",ans);
return 0;
}