题解:AT_abc147_f [ABC147F] Sum Difference

题意

在一个等差数列中取出若干个元素,求取出的元素与未取出的元素的差值有多少种可能。

思路

首先,我们有一个式子:
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)=iSAii/SAi
不难看出,该式可以变为:
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×iSAii=1nAi
其实, ∑ 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 iSAi 的不同和即可。

不难看出 A i = X + ( i − 1 ) × n u m A_i=X+(i-1)\times num Ai=X+(i1)×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×(t1),2(2n1t)×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 (titj)×x=(kjki)×num
可推出:
n u m ∣ ( t i − t j ) × x num \mid (t_i-t_j)\times x num(titj)×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;
}
  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值