P5305 [GXOI/GZOI2019] 旧词 & P4211 [LNOI2014] LCA 经典题目分析
P4211 [LNOI2014] LCA
分析
考虑深度与路径的关系,那么我们发现实际上我们求的是当前点到根的路径上有多少个在这个区间内根节点到他们的路径交点。
不难想到用分块维护,但是要卡常。
考虑把操作离线下来,转化为前缀和的形式,然后扫一遍即可。
考虑加点的时候怎么算到路径上。可以直接用树链剖分解决,跳上去是 \(\mathcal{O}(\log n)\) 次的,当然还要加上线段树更改。
代码
时间复杂度 \(\mathcal{O}(n\log^2 n)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 50004
using namespace std;
const int mod = 201314;
int tr[N << 2],lz[N << 2];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
void pushup(int x) {
tr[x] = (tr[ls(x)] + tr[rs(x)]) % mod;
}
void pushdown(int x,int l,int r) {
int mid = l + r >> 1;
lz[ls(x)] += lz[x],lz[rs(x)] += lz[x];
lz[ls(x)] %= mod,lz[rs(x)] %= mod;
tr[ls(x)] = (tr[ls(x)] + (mid - l + 1) * lz[x] % mod) % mod;
tr[rs(x)] = (tr[rs(x)] + (r - mid) * lz[x] % mod) % mod;
lz[x] = 0;
}
void update(int x,int l,int r,int L,int R,int val) {
if (l > R || r < L) return ;
if (L <= l & r <= R) {
lz[x] += val;
lz[x] %= mod;
tr[x] = (tr[x] + (r - l + 1) * val % mod) % mod;
return;
}
if (lz[x]) pushdown(x,l,r);
int mid = l + r >> 1;
update(ls(x),l,mid,L,R,val),update(rs(x),mid + 1,r,L,R,val);
pushup(x);
}
int query(int x,int l,int r,int L,int R) {
if (l > R || r < L) return 0;
if (L <= l && r <= R) return tr[x];
if (lz[x]) pushdown(x,l,r);
int mid = l + r >> 1;
return query(ls(x),l,mid,L,R) + query(rs(x),mid + 1,r,L,R);
}
vector<int> g[N];
int dep[N],sz[N],son[N],L[N],R[N],cnt,top[N],n,father[N],q;
void dfs0(int cur,int fa) {
dep[cur] = dep[fa] + 1;
sz[cur] = 1;
for (auto i : g[cur]) {
dfs0(i,cur);
sz[cur] += sz[i];
if (sz[i] > sz[son[cur]]) son[cur] = i;
}
}
void dfs1(int cur,int tp) {
top[cur] = tp;
L[cur] = ++cnt;
if (son[cur]) dfs1(son[cur],tp);
for (auto i : g[cur])
if (i != son[cur]) dfs1(i,i);
R[cur] = cnt;
}
void update_path(int x) {
while(x) {
update(1,1,n,L[top[x]],L[x],1);
x = father[top[x]];
}
}
int query_path(int x) {
int res = 0;
while(x) {
res = (res + query(1,1,n,L[top[x]],L[x])) % mod;
x = father[top[x]];
}
return res;
}
struct que{
int ed,x,type,id;
}qu[N << 1];
int ans[N];
signed main(){
cin >> n >> q;
for (int i = 2;i <= n;i ++) {
scanf("%lld",&father[i]);
father[i] ++;
g[father[i]].push_back(i);
}
dfs0(1,0),dfs1(1,0);
int tot = 0;
for (int i = 1;i <= q;i ++) {
int l,r,x;
scanf("%lld%lld%lld",&l,&r,&x);
l ++,r ++,x ++;
qu[++tot] = {r,x,1,i};
if (l > 1) qu[++tot] = {l - 1,x,-1,i};
}
stable_sort(qu + 1,qu + 1 + tot,[](que x,que y) {
return x.ed < y.ed;
});
int j = 1;
for (int i = 1;i <= tot;i ++) {
int qed = qu[i].ed,qid = qu[i].id;
while(j <= qed) update_path(j),j ++;
ans[qid] = (ans[qid] + qu[i].type * query_path(qu[i].x) % mod + mod) % mod;
}
for (int i = 1;i <= q;i ++) printf("%lld\n",ans[i]);
return 0;
}
分块代码。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <cmath>
#define int long long
#define N 50005
#define M 350
using namespace std;
const int mod = 201314;
vector<int> g[N];
int n,m,fa[N][20],sz[N],dep[N];
void dfs0(int cur,int father) {
sz[cur] = 1;
dep[cur] = dep[father] + 1;
for (auto i : g[cur]) dfs0(i,cur),sz[cur] += sz[i];
}
int LCA(int x,int y) {
if (x == y) return x;
if (dep[x] < dep[y]) x ^= y ^= x ^= y;
for (int j = 17;j >= 0;j --)
if (dep[fa[x][j]] >= dep[y]) x = fa[x][j];
if (x == y) return x;
for (int j = 17;j >= 0;j --)
if (fa[x][j] != fa[y][j]) x = fa[x][j],y = fa[y][j];
return fa[x][0];
}
int dis(int x,int y) {
return dep[x] + dep[y] - 2 * dep[LCA(x,y)];
}
int bl[N],L[M],R[M],val[N],b[N],ans[N][M];
void dfs1(int cur) {
for (auto i : g[cur]) dfs1(i),val[cur] = (val[cur] + val[i]) % mod;
}
void dfs2(int cur,int w) {
b[cur] = val[cur] + w;
for (auto i : g[cur]) dfs2(i,b[cur]);
}
signed main(){
cin >> n >> m;
for (int i = 2;i <= n;i ++) {
scanf("%lld",&fa[i][0]);
fa[i][0] ++;
g[fa[i][0]].push_back(i);
}
dfs0(1,0);
for (int j = 1;j <= 17;j ++)
for (int i = 1;i <= n;i ++)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
int len = 150;
for (int i = 1;i <= n;i ++) {
bl[i] = i / len + 1;
if (!L[bl[i]]) L[bl[i]] = i;
R[bl[i]] = i;
}
for (int i = 1;i <= bl[n];i ++) {
for (int j = 1;j <= n;j ++) val[j] = b[j] = 0;
for (int j = L[i];j <= R[i];j ++) val[j] ++;
dfs1(1),dfs2(1,0);
for (int j = 1;j <= n;j ++) ans[j][i] = b[j];
}
for (int l,r,x;m --;) {
scanf("%lld%lld%lld",&l,&r,&x);
l ++,r ++,x ++;
if (bl[l] == bl[r]) {
int res = 0;
for (int i = l;i <= r;i ++) res = (res + dep[LCA(x,i)]) % mod;
printf("%lld\n",res);
continue;
}
int res = 0;
for (int i = l;i <= R[bl[l]];i ++) res = (res + dep[LCA(x,i)]) % mod;
for (int i = L[bl[r]];i <= r;i ++) res = (res + dep[LCA(x,i)]) % mod;
for (int i = bl[l] + 1;i < bl[r];i ++) res = (res + ans[x][i]) % mod;
printf("%lld\n",res);
}
return 0;
}
P5305 [GXOI/GZOI2019] 旧词
上一题的加强版,带了 \(k\) 次方。
分析
我们还是考虑加路径。
考虑最后的路径为:
4 - 1
3 - 2
3 - 3
2 - 4
1 - 5
1 - 6
我们想要得到 \(1\) 个 \(6^k\),\(1\) 个 \(4^k\),\(1\) 个 \(3^k\) 和 \(1\) 个 \(1^k\)。
不难想到拆成:
所贡献的:
1 1 1 1 - 1
1 1 1 - 2
1 1 1 - 3
1 1 - 4
1 - 5
1 - 6
还是考虑求和乘上贡献,发现这个前缀和可以用一个 \(k\) 次方差分抵消成第 \(x\) 项,也就是令第 \(x\) 层的贡献系数为 \(x^k-(x-1)^k\),然后就能差分了。
这样这道题目就做完了。
代码
时间复杂度 \(\mathcal{O}(n\log^2 n)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 50005
using namespace std;
const int mod = 998244353;
int qpow(int a,int b) {
int res = 1;
while(b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
struct node{
int sum,val;
}tr[N << 2];
int lz[N << 2];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
void pushup(int x) {
tr[x].sum = (tr[ls(x)].sum + tr[rs(x)].sum) % mod;
}
void pushdown(int x) {
lz[ls(x)] = (lz[ls(x)] + lz[x]) % mod,lz[rs(x)] = (lz[rs(x)] + lz[x]) % mod;
tr[ls(x)].sum = (tr[ls(x)].sum + tr[ls(x)].val * lz[x] % mod) % mod;
tr[rs(x)].sum = (tr[rs(x)].sum + tr[rs(x)].val * lz[x] % mod) % mod;
lz[x] = 0;
}
void update(int x,int l,int r,int L,int R,int val) {
if (l > R || r < L) return;
if (L <= l && r <= R) {
lz[x] = (lz[x] + val) % mod;
tr[x].sum = (tr[x].sum + tr[x].val * val % mod) % mod;
return;
}
if (lz[x]) pushdown(x);
int mid = l + r >> 1;
update(ls(x),l,mid,L,R,val),update(rs(x),mid + 1,r,L,R,val);
pushup(x);
}
int query(int x,int l,int r,int L,int R) {
if (l > R || r < L) return 0;
if (L <= l && r <= R) return tr[x].sum;
if (lz[x]) pushdown(x);
int mid = l + r >> 1;
return (query(ls(x),l,mid,L,R) + query(rs(x),mid + 1,r,L,R)) % mod;
}
vector<int> g[N];
int n,q,dep[N],val[N],sz[N],L[N],R[N],top[N],cnt,son[N],father[N],k,rid[N];
void build(int x,int l,int r) {
if (l == r) return tr[x].val = val[rid[l]],void();
int mid = l + r >> 1;
build(ls(x),l,mid),build(rs(x),mid + 1,r);
tr[x].val = tr[ls(x)].val + tr[rs(x)].val;
}
void dfs(int cur,int fa) {
dep[cur] = dep[fa] + 1;
val[cur] = (qpow(dep[cur],k) - qpow(dep[fa],k) + mod) % mod;
sz[cur] = 1;
for (auto i : g[cur]) {
dfs(i,cur);
sz[cur] += sz[i];
if (sz[i] > sz[son[cur]]) son[cur] = i;
}
}
void dfs2(int cur,int tp) {
top[cur] = tp;
L[cur] = ++cnt;
rid[cnt] = cur;
if (son[cur]) dfs2(son[cur],tp);
for (auto i : g[cur])
if (i != son[cur]) dfs2(i,i);
R[cur] = cnt;
}
void update_path(int x) {
while(x) {
update(1,1,n,L[top[x]],L[x],1);
x = father[top[x]];
}
}
int query_path(int x) {
int res = 0;
while(x) {
res = (res + query(1,1,n,L[top[x]],L[x])) % mod;
x = father[top[x]];
}
return res;
}
struct que{
int ed,x,id;
}qu[N];
int ans[N];
signed main(){
cin >> n >> q >> k;
for (int i = 2;i <= n;i ++) {
scanf("%lld",&father[i]);
g[father[i]].push_back(i);
}
dfs(1,0);
dfs2(1,1);
build(1,1,n);
for (int i = 1;i <= q;i ++) {
int x,y;
scanf("%lld%lld",&x,&y);
qu[i] = {x,y,i};
}
stable_sort(qu + 1,qu + 1 + q,[](que x,que y) {
return x.ed < y.ed;
});
int j = 1;
for (int i = 1;i <= q;i ++) {
while(j <= qu[i].ed) update_path(j),j ++;
ans[qu[i].id] = query_path(qu[i].x);
}
for (int i = 1;i <= q;i ++) cout << ans[i] << '\n';
return 0;
}

浙公网安备 33010602011771号