题意
在一个等差数列中取出若干个元素,求取出的元素与未取出的元素的差值有多少种可能。
思路
首先,我们有一个式子:
w
(
i
)
=
∑
i
∈
S
A
i
−
∑
i
∉
S
A
i
w(i)=\sum_{i \in S}A_i-\sum_{i \notin S}A_i
w(i)=i∈S∑Ai−i∈/S∑Ai
不难看出,该式可以变为:
w
(
i
)
=
2
×
∑
i
∈
S
A
i
−
∑
i
=
1
n
A
i
w(i)=2\times \sum_{i \in S}A_i-\sum_{i=1}^{n}A_i
w(i)=2×i∈S∑Ai−i=1∑nAi
其实,
∑
i
=
1
n
+
A
i
\sum_{i=1}^{n}+A_i
∑i=1n+Ai 和
2
2
2 是一个定值,所以我们只需求出
∑
i
∈
S
A
i
\sum_{i \in S}A_i
∑i∈SAi 的不同和即可。
不难看出 A i = X + ( i − 1 ) × n u m A_i=X+(i-1)\times num Ai=X+(i−1)×num,其中 X X X 为首项, n u m num num 为公差。
所以,假如我们选取了 t t t 个数,那它们的总和必定为 t X + n u m × s tX+num \times s tX+num×s。
而 s s s 如何求?
不难看出,最小的肯定是选前 t t t 个,最大的则是选后 t t t 个,所以 s s s 的范围为 [ t × ( t − 1 ) 2 , ( 2 n − 1 − t ) × t 2 ] [\frac{t\times(t-1)}{2} ,\frac{(2n-1-t)\times t}{2}] [2t×(t−1),2(2n−1−t)×t]。
但是,我们还需要考虑覆盖的情况,也就是:
t
i
×
x
+
k
i
×
n
u
m
=
t
j
×
x
+
k
j
×
n
u
m
t_i\times x+k_i\times num=t_j\times x+k_j\times num
ti×x+ki×num=tj×x+kj×num
移项得:
(
t
i
−
t
j
)
×
x
=
(
k
j
−
k
i
)
×
n
u
m
(t_i-t_j)\times x=(k_j-k_i)\times num
(ti−tj)×x=(kj−ki)×num
可推出:
n
u
m
∣
(
t
i
−
t
j
)
×
x
num \mid (t_i-t_j)\times x
num∣(ti−tj)×x
可得
t
i
×
x
t_i \times x
ti×x 与
t
j
×
x
t_j \times x
tj×x 对模
n
u
m
num
num 同余。
所以,我们可以直接把 t i × x t_i \times x ti×x 和 t j × x t_j \times x tj×x 余数相等的放到一起,求一下区间覆盖即可。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,x,num,ans;
struct node{
int l,r;
bool operator<(const node& x)const{return l<x.l;}
};
vector<node> v[2000005];
map<int,int> M;
int cnt;
signed main(){
scanf("%lld%lld%lld",&n,&x,&num);
if(!num){
if(!x) puts("1");
else printf("%lld\n",n+1);
return 0;
}
for(int t=0;t<=n;t++){
if(!M[t*x%num]) M[t*x%num]=++cnt;
v[M[t*x%num]].push_back({t*(t-1)/2+(t*x/num),(2*n-1-t)*t/2+(t*x/num)});
}
for(int i=1;i<=cnt;i++){
sort(v[i].begin(),v[i].end());
int L=v[i][0].l,R=v[i][0].r;
for(int j=1;j<v[i].size();j++){
if(v[i][j].l<=R) R=max(v[i][j].r,R);
else ans+=(R-L+1),L=v[i][j].l,R=v[i][j].r;
}
ans+=(R-L+1);
}
printf("%lld\n",ans);
return 0;
}