P9844 [ICPC2021 Nanjing R] Paimon Segment Tree
xiezheyuan · · 题解
简要题意
你需要维护一个
有
有
思路
先差分,改为求
考虑区间和
其中
不难发现假如我们用向量来表示每一个位置:
则可以构造一个矩阵,用矩阵乘法的方法来修改:
然后需要注意下没有修改的部分,区间历史平方和需要集体手动更新:
则可以构造一个矩阵,用矩阵乘法的方法来修改:
然后你抓一只无辜的线段树维护一下即可。
至于询问,可以离线,在每一次操作后计算一下需要计算的区间历史平方和。
时间复杂度
实现上的小细节
- 输入的
v 有负数,需要注意取模姿势。否则容易 WA 在第3 个点。 - 尽量减少取模次数,对于加法只需要做一次减法来代替直接取模,对于乘法没必要使用
(1ll * x * y % mod + mod) % mod直接使用1ll * x * y % mod即可。 - 除了乘法,其他地方不要开
long long。
代码
喜提最优解最后一名!此代码目前只能在 Luogu 上通过,Gym 上被卡常了,开心。
Submission
#include <bits/stdc++.h>
#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;
const int mod = 1e9 + 7, N = 5e4 + 5;
int M(long long x){return x%mod;}
int Add(long long x, long long y){return (x + y) > mod ? (x - mod + y) : (x + y);}
struct matrix{
int n,m,a[5][5];
void clear(){memset(a, 0, sizeof(a));}
void init(int N, int M){n = N, m = M;clear();}
int* operator[](int i){return a[i];}
};
matrix operator*(matrix a, matrix b){
matrix ans;ans.init(a.n, b.m);
assert(a.m == b.n);
for(int k=1;k<=a.m;k++){
for(int i=1;i<=a.n;i++){
for(int j=1;j<=b.m;j++) ans[i][j] = Add(ans[i][j], M(1ll * a[i][k] * b[k][j]));
}
}
return ans;
}
matrix operator+(matrix a, matrix b){
matrix ans;ans.init(a.n, a.m);
for(int i=1;i<=a.n;i++){
for(int j=1;j<=a.m;j++) ans[i][j] = Add(a[i][j], b[i][j]);
}
return ans;
}
matrix t[N << 2], tag[N << 2];
void pushup(int i){t[i] = t[ls] + t[rs];}
void build(int i, int l, int r){
tag[i].init(4, 4);
tag[i][1][1] = tag[i][2][2] = tag[i][3][3] = tag[i][4][4] = 1;
if(l == r){
int v;cin>>v;
t[i].init(1, 4);
t[i][1][1] = 1;t[i][1][2] = v = Add(mod, v);
t[i][1][3] = t[i][1][4] = M(1ll * v * v);
return;
}
build(ls, l, mid);build(rs, mid + 1, r);
pushup(i);
}
void pushdown(int i){
tag[ls] = tag[ls] * tag[i];
tag[rs] = tag[rs] * tag[i];
t[ls] = t[ls] * tag[i];
t[rs] = t[rs] * tag[i];
tag[i].init(4, 4);
tag[i][1][1] = tag[i][2][2] = tag[i][3][3] = tag[i][4][4] = 1;
}
void update(int ql, int qr, matrix v, int i, int l, int r){
if(ql > qr) return;
if(ql <= l && r <= qr){
tag[i] = tag[i] * v;
t[i] = t[i] * v;
return;
}
pushdown(i);
if(ql <= mid) update(ql, qr, v, ls, l, mid);
if(qr > mid) update(ql, qr, v, rs, mid + 1, r);
pushup(i);
}
matrix query(int ql, int qr, int i, int l, int r){
if(ql <= l && r <= qr) return t[i];
pushdown(i);
if(ql <= mid && qr > mid) return query(ql, qr, ls, l, mid) + query(ql, qr, rs, mid + 1, r);
if(ql <= mid) return query(ql, qr, ls, l, mid);
else return query(ql, qr, rs, mid + 1, r);
}
vector<pair<int,int> > qs[N];
vector<int> ans[N];
int n,m,q;
struct Update{
int l, r, v;
} updates[N];
struct Query{
int p1, p2, q1, q2;
} qqs[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>q;
build(1, 1, n);
for(int i=1;i<=m;i++) cin>>updates[i].l>>updates[i].r>>updates[i].v;
for(int i=1;i<=q;i++){
int l,r,x,y;cin>>l>>r>>x>>y;
qqs[i].p1 = qs[x].size();
qqs[i].q1 = x;
qs[x].push_back(make_pair(l, r));
qqs[i].p2 = qs[y + 1].size();
qqs[i].q2 = y + 1;
qs[y + 1].push_back(make_pair(l, r));
}
for(auto i : qs[0]) ans[0].push_back(0);
for(auto i : qs[1]) ans[1].push_back(query(i.first, i.second, 1, 1, n)[1][4]);
for(int i=1;i<=m;i++){
int l = updates[i].l, r = updates[i].r, v = updates[i].v;
matrix mat;mat.init(4, 4);
mat[1][1] = mat[2][2] = mat[3][3] = mat[3][4] = mat[4][4] = 1;
mat[1][2] = v = Add(mod, v);
mat[1][3] = mat[1][4] = M(1ll * v * v);
mat[2][3] = mat[2][4] = Add(v, v);
update(l, r, mat, 1, 1, n);
mat.clear();
mat[1][1] = mat[2][2] = mat[3][3] = mat[3][4] = mat[4][4] = 1;
update(1, l - 1, mat, 1, 1, n);
update(r + 1, n, mat, 1, 1, n);
for(auto j : qs[i + 1]) ans[i + 1].push_back(query(j.first, j.second, 1, 1, n)[1][4]);
}
for(int i=1;i<=q;i++) cout<<Add(ans[qqs[i].q2][qqs[i].p2], mod - ans[qqs[i].q1][qqs[i].p1])<<'\n';
return 0;
}