ABC455F 题解

洛谷没投上,生气生气。

最近这场 ABC 表现终于还算能看。

首先关注怎么合并能使总代价最小。第一反应是类似石子合并一样先合并小的。

但是尝试一组数据。

1 2 4

先合并 \(1\)\(2\),再合并 \(3\)\(4\),代价为 \(1 \times 2 + 3 \times 4 = 14\)

先合并 \(2\)\(4\),再合并 \(1\)\(6\),代价为 \(2 \times 4 + 1 \times 6 = 14\)

大胆猜测不论合并顺序,最后总代价相等。

稍加分析,设我们将 \(a,b,c,d\) 依次合并起来,那么代价是:

\[ab+(a+b)c+(a+b+c)d = ab+ac+ad+bc+bd+cd \]

尝试其他合并方式,发现无论怎么合并,最终的代价都是元素两两相乘再求和。但是这个值在区间加的操作上不好维护。

接下来转化。

\[\sum_{i = 1}^{n} \sum_{j = i + 1}^{n} a_ia_j = \frac{1}{2}\left(\sum_{i=1}^{n} \sum_{j=1}^{n} a_ia_j - \sum_{i = 1}^{n}a_i^2 \right) = \frac{1}{2}\left(\left(\sum_{i=1}^{n}a_i \right)^2 - \sum_{i = 1}^{n}a_i^2 \right) \]

这样我们只用维护区间和 \(x\) 与区间平方和 \(y\)

假设要给区间 \([l , r]\) 加上一个 \(v\)

\[newx = \sum_{i=l}^{r}(a_i+v) = (r-l+1)v+\sum_{i=l}^{r}a_i = (r-l+1)v + x \]

区间和简单,区间平方和怎么维护?

\[newy = \sum_{i = l}^{r}(a_i+v)^2 = \sum_{i = l}^{r} a_i^2+\sum_{i = l}^{r}2a_iv+\sum_{i = l}^{r}v^2=y+2vx+(r-l+1)v^2 \]

这样,在线段树上维护 \(x,y\),就能以 \(O(q \log n)\) 的时间复杂度解决问题。最后的答案就是 \(\frac{1}{2}(x^2-y)\)

代码使用动态开点线段树。最后算答案的式子里有 \(\frac{1}{2}\) ,在模意义下就是乘 \(2\) 的逆元。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mp make_pair
#define fo(i , x , y) for(int i = x ; i <= y ; i++)
#define go(i , x , y) for(int i = x ; i >= y ; i--)
using namespace std;
const int maxn = 100000;
const int mod = 998244353;
const int inv2 = 499122177;
struct node{
    long long x , y;
    friend node operator + (const node a , const node b){
        return {(a.x + b.x) % mod , (a.y + b.y) % mod};
    }
};
struct Segment_Tree{
    private:
    int cnt , ls[maxn * 2 + 5] , rs[maxn * 2 + 5];
    long long lazy[maxn * 2 + 5];
    node t[maxn * 2 + 5];
    void modify(long long v1 , int l , int r , int k){
        t[k].y = t[k].y + 2 * v1 * t[k].x % mod + (r - l + 1) * v1 % mod * v1 % mod;
        t[k].y %= mod;
        t[k].x = (t[k].x + (r - l + 1) * v1 % mod) % mod;
        lazy[k] += v1;
        lazy[k] %= mod;
    }
    void pushdown(int l , int r , int k){
        int mid = (l + r) / 2;
        if(ls[k] == 0) ls[k] = build(l , mid);
        if(rs[k] == 0) rs[k] = build(mid + 1 , r);
        if(lazy[k] == 0) return;
        modify(lazy[k] , l , mid , ls[k]);
        modify(lazy[k] , mid + 1 , r , rs[k]);
        lazy[k] = 0;
    }
    public:
    int build(int l , int r){
        int k = ++cnt;
        return k;
    }
    void update(int L , int R , long long v1 , int l , int r , int k){
        // cerr << "update:" << L << " " << R << " " << l << " " << r << "\n";
        if(L > R) return;
        if(L <= l and r <= R){
            modify(v1 , l , r , k);
            return;
        }
        pushdown(l , r , k);
        int mid = (l + r) / 2;
        if(L <= mid) update(L , R , v1 , l , mid , ls[k]); 
        if(mid + 1 <= R) update(L , R , v1 , mid + 1 , r , rs[k]);
        t[k] = t[ls[k]] + t[rs[k]];
    }
    node query(int L , int R , int l , int r , int k){
        // cerr << L << " " << R << " " << l << " " << r << "\n";
        if(L <= l and r <= R){
            // cerr << "t[k]:" << t[k].x << " " << t[k].y << "\n";
            return t[k];
        }
        pushdown(l , r , k);
        int mid = (l + r) / 2;
        if(L <= mid){
            node ans = query(L , R , l , mid , ls[k]);
            if(mid + 1 <= R) return ans + query(L , R , mid + 1 , r , rs[k]);
            else return ans;
        }
        else return query(L , R , mid + 1 , r , rs[k]);
    }
}tr;
int n , q;
int main(){
    ios :: sync_with_stdio(0) , cin.tie(0) , cout.tie(0);
    // freopen(".in" , "r" , stdin);
    // freopen(".out" , "w" , stdout);
    cin >> n >> q;
    int rt = tr.build(1 , n);
    fo(i , 1 , q){
        int l , r , a;
        cin >> l >> r >> a;
        tr.update(l , r , a , 1 , n , rt);
        node tmp = tr.query(l , r , 1 , n , rt);
        long long sum = tmp.x , sum2 = tmp.y;
        // cerr << sum << " " << sum2 << "\n";
        cout << ((sum * sum % mod - sum2) % mod + mod) % mod * inv2 % mod << "\n";
    }
}
posted @ 2026-04-28 19:20  Natho_nA  阅读(13)  评论(0)    收藏  举报