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";
}
}

浙公网安备 33010602011771号