ferinの競プロ帳

競プロについてのメモ

AtCoder Beginner Contest 147 F - Sum Difference

問題ページ
0-originで考える.S+T = \sum_{i=0}^{n-1} X+iD より,S-T = 2S-\text{sum} となる.\text{sum} は一定なので,結局 S が取りうる値の種類数を数えられればよいことがわかる.

例として要素を3個 (=p,q,r) 選んだときの S について考える.これは S = 3X + D(p+q+r) となる.p+q+r が取りうる値は,0+1+2 以上,n-3+n-2+n-1 以下となる.p,q,r は1刻みで値を変えられるので,うまく値を動かすと p+q+r0+1+2 以上 n-3+n-2+n-1 以下の全ての値を取ることが出来る.一般に要素を k 個選んだ場合,0+1+\cdots+i-1 以上 n-1-i+n-i+\cdots+n-1 以下の全ての値を取ることができる.

kX + D \times ( 選んだ要素の和 ) が取る値の mod D は一定で,kX + Dl, kX + D(l+1), \ldots, kX + Dr となる.mod の値ごとに (l,r) の値を保存しておき,この和集合を取ればよい.(l,r) をソートして,隣接する区間が交差するならマージする操作を繰り返すことでこれは求まる.

mod D で分類するのが思いつかなかった…

#include <bits/stdc++.h>  
using namespace std;  
using ll = long long;  
using PII = pair<ll, ll>;  
#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)  
#define REP(i, n) FOR(i, 0, n)  
#define ALL(x) x.begin(), x.end()  
template<typename T> void chmin(T &a, const T &b) { a = min(a, b); }  
template<typename T> void chmax(T &a, const T &b) { a = max(a, b); }  
struct FastIO {FastIO() { cin.tie(0); ios::sync_with_stdio(0); }}fastiofastio;  
#ifdef DEBUG_   
#include "../program_contest_library/memo/dump.hpp"  
#else  
#define dump(...)  
#endif  
const ll INF = 1LL<<60;  
  
int main(void) {  
    ll n, x, d;  
    cin >> n >> x >> d;  
  
    if(d == 0) {  
        if(x == 0) cout << 1 << endl;  
        else cout << n+1 << endl;  
        return 0;  
    }  
  
    if(d < 0) x *= -1, d *= -1;  
  
    map<ll,vector<PII>> mp;  
    REP(i, n+1) {  
        // i個選ぶとき  
        // i*x + d(0 + 1 + … + i-1) ~ i*x + d(n-1-i + n-i + … + n-1)  
        ll lb = i*x/d + i*(i-1)/2;  
        ll ub = i*x/d + n*i - i*(i+1)/2;  
        ll mo = ((i*x)%d+d)%d;  
        mp[mo].emplace_back(lb, ub);  
    }  
  
    ll ret = 0;  
    for(auto pv: mp) {  
        auto v = pv.second;  
        sort(ALL(v));  
        PII last = v[0];  
        vector<ll> nv;  
        FOR(i, 1, v.size()) {  
            if(v[i].first <= last.second) {  
                chmax(last.second, v[i].second);  
            } else {  
                ret += last.second - last.first + 1;  
                last = v[i];  
            }  
        }  
        ret += last.second - last.first + 1;  
    }  
    cout << ret << endl;  
  
    return 0;  
}