题解 P7334 [JRKSJ R1] 吊打

· · 题解

区间操作首先想到线段树。

和 GSS4 一样,可以用相同的方法,记录区间最大值,如果这个最大值 >1 才进入区间修改。10^9 做五次开方操作就会变成 1,所以复杂度是有保证的。

显然,区间平方的和 与 区间和的平方不是一个东西,无法直接修改,要考虑别的做法。平方和开方是相反的操作,平方再开方后原数是不变的,考虑对每个点记录一个 \text{cnt}_i 表示当前这个数 \text{val}_i 被平方了多少次。对于每次开方操作,将 \text{cnt}_i 区间 +1。开方时,若 \text{cnt}_i 不为 0\text{cnt}_i = \text{cnt}_i-1,否则将原数开方。因为 1 平方和开方后都是 1,所以这种做法区间最大值在 >1 上的正确性是有保证的。

但是这样还不足以通过。通过一次平方一次开方的操作可以将这种做法卡到 O(nm)。再维护区间 \text{cnt}_i 的最小值,修改时,若这个最小值 \geq 1,则直接区间将 \text{cnt}_i = \text{cnt}_i - 1。否则进入左右区间修改。

```cpp #define ls id << 1 #define rs id << 1 | 1 #define Mid int mid = (l + r) >> 1 def(nn, int, 2e5 + 5) def(N, int, 8e5 + 5) def(p, int, 998244353) int n, q; int a[nn]; ll val[N], cnt[N], lz[N], mx[N], mnc[N]; void pushup(int id) { mx[id] = max(mx[ls], mx[rs]); mnc[id] = min(mnc[ls], mnc[rs]); } void pushdown(int id) { if(lz[id]) { cnt[ls] += lz[id]; cnt[rs] += lz[id]; mnc[ls] += lz[id]; mnc[rs] += lz[id]; lz[ls] += lz[id]; lz[rs] += lz[id]; lz[id] = 0; } } void build(int id, int l, int r) { if(l == r) { val[id] = mx[id] = a[l]; return ; } Mid; build(ls, l, mid); build(rs, mid + 1, r); pushup(id); } void update(int id, int l, int r, int x, int y) { if(x <= l && r <= y && mnc[id] >= 1) { --cnt[id], --mnc[id], --lz[id]; return ; } if(l == r) { if(cnt[id]) --cnt[id], --mnc[id]; else mx[id] = val[id] = sqrt(val[id]); return ; } pushdown(id); Mid; if(mx[ls] > 1 && mid >= x) update(ls, l, mid, x, y); if(mx[rs] > 1 && mid < y) update(rs, mid + 1, r, x, y); pushup(id); } void modify(int id, int l, int r, int x, int y) { if(x <= l && r <= y) { ++cnt[id], ++mnc[id], ++lz[id]; return ; } pushdown(id); Mid; if(mid >= x) modify(ls, l, mid, x, y); if(mid < y) modify(rs, mid + 1, r, x, y); pushup(id); } ll query(int id, int l, int r, int x) { if(l == r) { int pf = qpow(cnt[id], 2, p - 1); return qpow(pf, val[id], p); } pushdown(id); Mid; if(mid >= x) return query(ls, l, mid, x); else return query(rs, mid + 1, r, x); } int main() { qread(n, q); rep(i, 1, n) qread(a[i]); build(1, 1, n); W(q) { int op, l, r; qread(op, l, r); if(op == 1) { update(1, 1, n, l, r); // rep(i, l, r) { // ll x = query(1, 1, n, i); // cout << x << endl; // } } else modify(1, 1, n, l, r); } ll ans = 0; rep(i, 1, n) { ll x = query(1, 1, n, i); //cout << x << endl; (ans += x) %= p; } printf("%lld\n", ans); return 0; } ```