这真是一道分块神题!
思路
我们先将点编号进行分块
令 \(b[i]\) 表示 \(i\) 的祖先中,最近的不与 \(i\) 同一个块的结点编号
显然,如果 \(pos[a[i]]<pos[i]\),那么 \(b[i] = a[i]\);否则 \(b[i] = b[a[i]]\)(\(pos[i]\) 表示 \(i\) 所在的块的编号)
考虑一个修改如何进行:
首先,对于零碎的块,我们直接暴力修改 \(a[i]\) 和 \(b[i]\)
对于完整的块,我们很难对块进行标记
但我们发现,当 \(pos[a[i]]<pos[i]\) 时,由于 \(a[i]\) 与 \(b[i]\) 相等,\(b[i]\) 就可以直接减去 \(x\)
对于一个块,要使里面的元素都有 \(a[i]=b[i]\),那么最多只需要减 \(B\) 次(\(B\) 表示块的大小)
因此,我们考虑用 \(cnt[i]\) 表示第 \(i\) 个块整体已经减了的数量,如果 \(cnt[i]<B\),那么我们就对块中每个结点暴力修改;否则,我们将 \(x\) 加到 \(tag[i]\) 中,表示 \(i\) 块内的结点的 \(b\) 值直接减去的数值
这样就将复杂度依旧均摊到 \(O(n\sqrt n)\)
对于查询,方法类似于树剖求 \(LCA\) 的方法:
-
当 \(pos[x]\not=pos[y]\) 时,\(x\) 和 \(y\) 中编号大的跳到 \(b\) 上
-
当 \(pos[x]\not=pos[y]\) 且 \(b[x]\not= b[y]\) 时,\(x\) 到 \(b[x]\),\(y\) 到 \(b[y]\)
-
当 \(pos[x]\not=pos[y]\) 且 \(b[x]= b[y]\) 时,\(x\) 和 \(y\) 编号大的跳到自己的父亲处
单次询问复杂度 \(O(\sqrt n)\)
(注意,\(tag\) 有可能爆 int
!)
代码
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int reads()
{
int sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
const int B = 310;
const int K = 100000 / B + 5;
int n, q, a[100005], b[100005];
int pos[100005], L[K], R[K];
int cnt[K], tag[K];
inline int fa(int x) {return std::max(a[x] - tag[pos[x]], 1);}
inline int jp(int x) {return std::max(b[x] - tag[pos[x]], 1);}
inline int LCA(int x, int y)
{
while(x != y)
{
if(pos[x] < pos[y]) y = jp(y);
else if(pos[x] > pos[y]) x = jp(x);
else
{
if(jp(x) != jp(y)) x = jp(x), y = jp(y);
else x > y ? x = fa(x) : y = fa(y);
}
}
return x;
}
inline void modify(int l, int r, int x)
{
FOR(i, pos[l] + 1, pos[r] - 1)
{
if(cnt[i] + x <= B)
{
FOR(j, L[i], R[i])
a[j] = std::max(1, a[j] - x),
b[j] = pos[a[j]] < pos[j] ? a[j] : b[a[j]];
cnt[i] += x;
}
else if(cnt[i] < B)
{
int sub = B - cnt[i];
FOR(j, L[i], R[i])
a[j] = std::max(1, a[j] - sub),
b[j] = pos[a[j]] < pos[j] ? a[j] : b[a[j]];
cnt[i] = B;
tag[i] += x - sub, tag[i] = std::min(tag[i], n);
}
else tag[i] += x, tag[i] = std::min(tag[i], n);
}
if(pos[l] < pos[r])
{
FOR(i, l, R[pos[l]])
a[i] = std::max(1, a[i] - x),
b[i] = pos[a[i]] < pos[i] ? a[i] : b[a[i]];
FOR(i, L[pos[r]], r)
a[i] = std::max(1, a[i] - x),
b[i] = pos[a[i]] < pos[i] ? a[i] : b[a[i]];
}
else
{
FOR(i, l, r)
a[i] = std::max(1, a[i] - x),
b[i] = pos[a[i]] < pos[i] ? a[i] : b[a[i]];
}
FOR(i, r + 1, R[pos[r]])
b[i] = pos[a[i]] < pos[i] ? a[i] : b[a[i]];
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = reads(), q = reads(), a[1] = pos[1] = 1;
FOR(i, 2, n) a[i] = reads(), pos[i] = (i - 1) / B + 1, b[i] = pos[a[i]] < pos[i] ? a[i] : b[a[i]];
FOR(i, 1, pos[n]) L[i] = R[i - 1] + 1, R[i] = R[i - 1] + B;
R[pos[n]] = n;
FOR(i, 1, q)
{
int ty = reads();
if(ty == 1)
{
int l = reads(), r = reads(), x = reads();
modify(l, r, x);
}
else
{
int u = reads(), v = reads();
printf("%d\n", LCA(u, v));
}
}
return 0;
}
标签:CF1491H,cnt,int,jp,Dynamic,pos,Ling,include,define
From: https://www.cnblogs.com/zuytong/p/16651154.html