D - 1D Country

对于每次询问
二分出对应的L村庄编号和R村庄编号
村庄编号为前缀和下标,前缀和处理区间和。

E - I Hate Sigma Problems

经典思想——拆位
由于每个元素 $x$ 对区间的贡献独立,所以可以单独考虑每种元素的贡献。
对于元素 $x$

typedef long long ll;
int n, pre[N];

void solve() {
    cin >> n;
    ll ans = 0;
    for (int i = 1; i <= n; i ++ ){
        int x;
        cin >> x;
        ans += 1ll * (i - pre[x]) * (n - i + 1);
        pre[x] = i;
    }
    cout << ans;
}
typedef long long ll;
int n;
vector<int> v[N];

void solve() {
    cin >> n;
    for (int i = 1; i <= n; i ++ ) v[i].push_back(0);
    for (int i = 1; i <= n; i ++ ){
        int x;
        cin >> x;
        v[x].push_back(i);
    }
    for (int i = 1; i <= n; i ++ ) v[i].push_back(n + 1);
    ll ans = 0;
    for (int x = 1; x <= n; x ++ ){
        ll r0 = 0; // 非法方案
        for (int i = 0; i + 1 < v[x].size(); i ++ ){
            int l = v[x][i] + 1, r = v[x][i + 1] - 1;
            if (l > r) continue;
            ll len = r - l + 1;
            r0 += len * (len - 1) / 2 + len;
        }
        ans += 1ll * n * (n - 1) / 2 + n - r0;
    }
    cout << ans;
}

F - Takahashi in Narrow Road

难点解析:不能越位,像是在一条轨道上推积木.

Tip

经典套路
对于第 $i$ 人坐标 $x_i$, 定义 $新x_i = x_i - i$

旧 $x_i$ 单调递增使得新 $x_i$ 单调不递减,很难不让人想到 $log$ 级别的做法
给定 $(t, g)$, 分讨推动 $t$ 的方向
如果是 $x_t \leq g$ ,需要推动所有在 $[x_t, g]$ 的 $x_i$.否则需要推动所有在 $[g, x_t]$ 的$x_i$.
贪心最小,一定成距离差1的块,因下标同样差1,所以最终区间内 $x_i$ 相同且为 $g-t$,消耗的步数就是初末区间和的差值.
如何找到最终的区间左右端点呢,由于 $x_i$ 单调不递减,所以可以二分
综上,二分线段树

/*
区间修改+区间查询
维护:区间和 
操作:查询+区间赋值
*/

const int N = 2e5 + 10;
typedef long long ll;

ll n, q, x[N];

struct info{
    ll sum, sz;
    info(){sum = 0; sz = 1;}
    info(ll sum, ll sz):sum(sum), sz(sz){}
};

struct tag{
    ll d;
    tag(){d = -1;}
    tag(ll d):d(d){}
};

info operator+(const info &l, const info &r){
    return {l.sum + r.sum, l.sz + r.sz};
}

info operator+(const info &v, const tag &t){ // 区间修改
    return {v.sz * t.d, v.sz};
}

tag operator+(const tag &t1, const tag &t2){ //懒标记和并
    return {t2.d};
}

struct Node{
    tag t;
    info val;
} seg[N * 4];

void settag(int id, tag t){
    seg[id].val = seg[id].val + t;
    seg[id].t = seg[id].t + t;
}

void pushdown(int id){ // 每一步都需要标记下传
    if (seg[id].t.d != -1){ // 标记非空
        settag(id * 2, seg[id].t);
        settag(id * 2 + 1, seg[id].t);
        //初始化标记
        seg[id].t.d = -1;
    }
}

void update(int id){
    seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}

void build(int id, int l, int r){
    if (l == r){
        seg[id].val = info(x[l], 1);
        seg[id].t = tag(-1);
    }
    else{
        int mid = (l + r) / 2;
        build(id * 2, l, mid);
        build(id * 2 + 1, mid + 1, r);
        update(id);
    }
}

info query(int id, int l, int r, int ql, int qr){
    if (l == ql && r == qr){
        return seg[id].val;
    }
    int mid = (l + r) / 2;
    pushdown(id);
    if (qr <= mid)
        return query(id * 2, l, mid, ql, qr);
    else if (ql > mid)
        return query(id * 2 + 1, mid + 1, r, ql, qr);
    else
        return query(id * 2, l, mid, ql, mid) +
               query(id * 2 + 1, mid + 1, r, mid + 1, qr);
}

void modify(int id, int l, int r, int ql, int qr, tag t){
    if (l == ql && r == qr)
    {
        settag(id, t);
        return;
    }
    int mid = (l + r) / 2;
    pushdown(id);
    if (qr <= mid)
        modify(id * 2, l, mid, ql, qr, t);
    else if (ql > mid)
        modify(id * 2 + 1, mid + 1, r, ql, qr, t);
    else
    {
        modify(id * 2, l, mid, ql, mid, t);
        modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
    }
    update(id);
}

void solve() {
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> x[i], x[i] -= i;
    build(1, 1, n);
    cin >> q;
    ll ans = 0;
    while (q -- ){
        ll t, g;
        cin >> t >> g; g -= t;
        //讨论方向
        ll xt = query(1, 1, n, t, t).sum;
        if (xt <= g){ // 向右  //[xt, g]  
            int l = t, r = n;
            while (l < r){
                int mid = (l + r + 1) / 2;
                if (query(1, 1, n, mid, mid).sum <= g) l = mid;
                else r = mid - 1;
            }
            ans += g * (r - t + 1) - query(1, 1, n, t, r).sum;
            modify(1, 1, n, t, r, tag(g));
        }else{ //向左  //[g, xt]
            int l = 1, r = t;
            while (l < r){
                int mid = (l + r) / 2;
                if (query(1, 1, n, mid, mid).sum >= g) r = mid;
                else l = mid + 1;
            }
            ans += query(1, 1, n, l, t).sum - g * (t - l + 1);
            modify(1, 1, n, l, t, tag(g));
        }
    }
    cout << ans;
}