P6782 [Ynoi2008] rplexq
给定一棵树,查询 (l,r,x) 即求解 (sum_{i<j,i,jin [l,r]}[LCA(i,j)=x])
(n,qle 2cdot 10^5),复杂度 (mathcal O(n^{1.5})),空间要求 (mathcal O(n))
Solution
直观的想法对不同的 (deg) 执行不同的算法,小 (deg) 的部分等价于二维数点问题,通过分块来均摊复杂度可以做到 (mathcal O(qdeg+nsqrt{n})),但是大 (deg) 的部分不好做了。
考虑暴力一点,对于具体的一个大 (deg),考虑对整个序列进行染色,又可以等价于统计 (lsim r) 编号内的颜色平方和,假定此处的查询有 (t) 次,则单独的处理可以通过莫队解决,复杂度为 (mathcal O(nsqrt{t}))
设每个点处的查询次数为 (q_i),其 (deg) 为 (c_i),则处理一个点的复杂度为 (min(nsqrt{q_i},q_ic_i))
事实上,处理编号的部分的复杂度不应当为 (nsqrt{q_i}) 而应为 ( extrm{size}_isqrt{q_i})
于是复杂度为 (mathcal O(sum min{ extrm{size}_isqrt{q_i},q_ic_i}))
对每个点判断那个算法更优以执行做法即可得到一个感性上分析出来大概是 (mathcal O(n^{frac{8}{5}})) 的算法。
接下来考虑将每个点的重儿子拿掉(即单独做 RMQ),然后执行此算法,复杂度会降低,构造了一段时间,感觉只能卡到 (mathcal O(n^{1.5}))
接下来的部分是卡空间。
由于扫描线的部分需要离线,这意味着空间存储暴力做是 (mathcal O(qcdot deg)) 的。
由于查询次数过多所以空间处理很麻烦。
考虑对 ( m dfn) 序做扫描线,查询存储在父亲节点上,每次枚举扫到 (i) 的时候枚举其父亲并处理其父亲的查询即可。
注意到儿子的 ( m dfn) 序是连续的,这意味着本次查询需要差分减去的值就是之前计算的值,所以对每种查询记录一个 ( m bef) 就真的做完了。
(Code:)
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define mp make_pair
#define pi pair<int, int>
#define pb push_back
#define vi vector<int>
#define int long long
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 4e5 + 5 ;
const int M = 505 ;
int n, m, rt, idx, cnt, bef[N], sz[N], son[N], head[N] ;
int Fa[N], vis[N], deg[N], c[N], dfn[N] ;
struct E { int to, next ; } e[N << 1] ;
struct temp { int id, l, r, ans, bef, las ; } ;
int Ans[N] ;
vector<temp> Q[N] ; //存储父亲处的查询
vector<int> Son[N] ;
vector<int> D[N] ;
void add(int x, int y) {
e[++ cnt] = (E){ y, head[x] }, head[x] = cnt,
e[++ cnt] = (E){ x, head[y] }, head[y] = cnt,
++ deg[x], ++ deg[y] ;
}
int C(int x) {
return x * (x - 1) / 2 ;
}
namespace S1 {
int num, tot, L[M], R[M], Id[N], w[N], tag[M] ;
void init() {
num = sqrt(n) + 1, L[++ tot] = 1 ;
rep( i, 1, n ) {
Id[i] = tot ;
if(i % num == 0) R[tot] = i, L[++ tot] = i + 1 ;
} if(L[tot] > n) -- tot ;
R[tot] = n ;
}
void Add(int x) {
for(int i = x; i <= R[Id[x]]; ++ i) ++ w[i] ;
for(int i = Id[x] + 1; i <= tot; ++ i) ++ tag[i] ;
}
int qry(int x) { return tag[Id[x]] + w[x] ; }
void solve() {
init() ;
for(re int i = 1; i <= n; ++ i) {
int u = bef[i], las = 0 ;
for(auto &x : Q[u])
x.bef = (qry(x.r) - qry(x.l - 1)), x.las = x.bef ;
Add(u) ;
for(auto &x : Q[u])
x.bef = (qry(x.r) - qry(x.l - 1)) ;
for(int v : D[u]) {
for(auto &x : Q[v])
x.bef = (qry(x.r) - qry(x.l - 1)),
x.ans += C(x.bef - x.las) ;
if((v != son[Fa[v]]) && (!vis[Fa[v]])) continue ;
for(auto &x : Q[Fa[v]])
las = x.bef,
x.bef = (qry(x.r) - qry(x.l - 1)),
x.ans -= C(x.bef - las) ;
}
}
}
}
int st[N], col[N], top ;
namespace S2 {
int len, num, A[N], L[N], tot, d[N] ;
temp q[N] ;
int Num ;
bool cmp(temp x, temp y) {
return ((x.l / num) == (y.l / num)) ? x.r < y.r : x.l < y.l ;
}
void Add(int x) { Num += d[x], ++ d[x] ; }
void Del(int x) { -- d[x], Num -= d[x] ; }
void solve(int u) {
sort(st + 1, st + top + 1), tot = 0 ;
rep( i, 1, top ) A[i] = col[st[i]] ;
rep( i, 1, top ) d[A[i]] = 0 ;
rep( i, 1, top ) L[i] = st[i] ; L[++ top] = n + 1 ;
for(auto x : Q[u]) {
temp t = x ;
t.l = lower_bound(L + 1, L + top + 1, x.l) - L ;
t.r = upper_bound(L + 1, L + top + 1, x.r) - L - 1 ;
if(t.l <= t.r) q[++ tot] = t ;
}
if(!tot) return ;
num = n / sqrt(tot) + 1, Num = 0 ;
sort(q + 1, q + tot + 1, cmp) ;
int L = 1, R = 0 ;
for(re int i = 1; i <= tot; ++ i) {
while( R < q[i].r ) Add(A[++ R]) ;
while( L > q[i].l ) Add(A[-- L]) ;
while( R > q[i].r ) Del(A[R --]) ;
while( L < q[i].l ) Del(A[L ++]) ;
Ans[q[i].id] -= Num ;
}
}
}
void dfs(int x, int fa) {
sz[x] = 1 ;
Next(i, x) {
int v = e[i].to ; if(v == fa) continue ;
dfs(v, x), sz[x] += sz[v], Son[x].pb(v) ;
if(sz[v] >= sz[son[x]]) son[x] = v ;
}
}
void Get(int x, int fa, int co) {
st[++ top] = x, col[x] = co ;
Next(i, x) {
int v = e[i].to ; if(v == fa) continue ;
Get(v, x, co) ;
}
}
void Dfs(int x, int fa) {
Fa[x] = fa, dfn[x] = ++ idx, bef[idx] = x ;
if(son[x]) Dfs(son[x], x) ;
Next( i, x ) {
int v = e[i].to ; if(v == fa || v == son[x]) continue ;
Dfs(v, x) ;
} D[bef[idx]].pb(x) ; //D 处存入顺序为自下而上
int u = sz[x] - sz[son[x]], v = deg[x] ;
u = u * sqrt(c[x]), v = v * c[x] ;
if(v < u) vis[x] = 1 ;
else {
top = 0 ;
Next( i, x ) {
int v = e[i].to ; if(v == fa || v == son[x]) continue ;
Get(v, x, v) ;
}
S2::solve(x) ;
}
}
signed main()
{
n = gi(), m = gi(), rt = gi() ; int x, y, z ;
rep( i, 2, n ) x = gi(), y = gi(), add(x, y) ;
rep( i, 1, m )
x = gi(), y = gi(), z = gi(), ++ c[z],
Q[z].pb((temp){i, x, y, 0, 0, 0}) ;
dfs(rt, 0), Dfs(rt, 0) ;
S1::solve() ;
rep( i, 1, n ) for(auto x : Q[i]) Ans[x.id] += x.ans ;
rep( i, 1, m ) printf("%lld
", Ans[i] ) ;
return 0 ;
}