题解 P2671 【求和】

· · 题解

概述

一道数学题,用暴力做复杂度 O(n^2),但是可以发现存在一些性质,从而把复杂度降到 O(n)

分析

看看题目怎么说:

  1. x<y<z$,且 $y-x=z-y
  2. color_x=color_y

对于第二个条件,很好想,我们可以把相同颜色的元素归到一组,然后在每一个“组”中寻找“三元组”。

对于第一个条件,乍一看就是说要找到三个等距离的元素,其实我们发现,在满足第二个条件的情况下,其实就是要找到 x,z 奇偶性相同的二元组 (x,z)

我们于是想到,对于每一个相同颜色的“组”中,再把它分成下标为奇数和下标为偶数的两个组,对于每一个组,去计算所有 (x,z) 的分数。

怎么计算分数呢?一个有 n 个元素的颜色相同、下标奇偶相同的“组”,它的总分数等于

\sum_{i=1}^n x_i\times (y_i\times(n-2)+\sum_{j=1}^n y_j)

我们看一看式子的推导过程。

记答案为 ansy_i 为组中第 i 个数的下标,x_inumber\_y_i

ans=(x_1+x_2)(y_1+y_2)+(x_1+x_3)(y_1+y_3)+\cdots+(x_1+x_n)(y_1+y_n) +(x_2+x_3)(y_2+y_3)+\cdots+(x_2+x_n)(y_2+y_n) \cdots +(x_{n-1}+x_{n})(y_{n-1}+y_n)

发现第 j 排都有 x_j,可以把所有的 x_j\times x_i^{(i=[1,n],i\neq j)} 提取出来,剩下的就是一些乱七八糟的东西,总之你最后就会发现,你得到了

ans=x_1(y_1+y_2+y_1+y_3+\cdots+y_1+y_n) +x_2(y_2+y_1+y_2+y_3+\cdots+y_2+y_n) \cdots +x_{n-1}(y_{n-1}+y_1+\cdots+y_{n-1}+y_{n-2}+y_{n-1}+y_{n})

我们发现其实第 i 排就是 x_i 乘上 (n-1)y_i 加上 j=[1,n]\&\&j\neq iy_j 的和。其实我们把 (n-1)y_i 中拎一个出来丢到后面的 y_{j=1\sim n,\neq i} 里这排的答案就变成了

x_i\times(y_i\times(n-2)+\sum_{j=1}^{n}y_j)

于是最终的答案就得到了!

所以核心代码就是 ``` for(int i=1;i<=n;i++) ans+=x[i]*(sum+y[i]*(n-2)); ``` # 代码 ``` #include <bits/stdc++.h> using namespace std; #define int long long //要开long long const int N=1e5+5; struct node { int num,col,idx;//idx:原来的下标 }t[N]; bool cmp(node a,node b){ return a.col<b.col;//相同的颜色放到一起方便处理 } int x[N],y[N],ans; int solve(int l,int r){//[l,r]是相同的颜色 int n=0; int sum=0,ans=0; for(int i=l;i<=r;i++) if(t[i].idx&1)x[++n]=t[i].num,y[n]=t[i].idx;//如果下标是奇数那么把他存到x,y里当成一组 for(int i=1;i<=n;i++) sum+=y[i];//预处理sum for(int i=1;i<=n;i++) ans=(ans+x[i]*(sum+y[i]*(n-2)))%10007;//这里就要取模小心到时候答案不对 n=0,sum=0; for(int i=l;i<=r;i++) if((t[i].idx&1)==0)x[++n]=t[i].num,y[n]=t[i].idx;//如果下标是偶数那么把他存到x,y里当成一组 for(int i=1;i<=n;i++) sum+=y[i]; for(int i=1;i<=n;i++) ans=(ans+x[i]*(sum+y[i]*(n-2)))%10007; return ans; } signed main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) cin>>t[i].num,t[i].idx=i; for(int i=1;i<=n;i++) cin>>t[i].col; sort(t+1,t+n+1,cmp); int las=1; for(int i=2;i<=n+1;i++) if(t[i].col!=t[i-1].col){ ans=(ans+solve(las,i-1))%10007; las=i; } cout<<ans%10007<<endl; return 0; } ```