题意
分析
法一:吉司机线段树
这是一个在线的\(O( n + q \cdot \log^2 n)\)做法。
考虑维护节点到根的权值前缀和cost,那么查询的时候区间减去子树根节点的cost就是价值。
然后由于子树dfs序连续,转化成线段树的区间查询。
对区间查询,分为4种情况:
- 最大值都无价值,答案为0
- 最大值有价值,但次大值以后都无价值,那么答案就是最大值价值乘以最大值个数
- 最小值有价值,那么所有的都有价值,答案是sum-cost*区间长
- 以上条件都不满足,递归处理
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
data=10*data+ch-'0',ch=getchar();
return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;
const int MAXN=1e5+7;
struct Edge
{
int nx,to,w;
}E[MAXN];
int head[MAXN],ecnt;
void addedge(int x,int y,int w)
{
E[++ecnt].to=y,E[ecnt].w=w;
E[ecnt].nx=head[x],head[x]=ecnt;
}
int dfn[MAXN],siz[MAXN],cost[MAXN];
int idx[MAXN],clk;
void dfs(int x)
{
// cerr<<"dfsing "<<x<<endl;
dfn[x]=++clk;
siz[x]=1;
idx[clk]=x;
for(int i=head[x];i;i=E[i].nx)
{
int y=E[i].to,w=E[i].w;
cost[y]=cost[x]+w;
dfs(y);
siz[x]+=siz[y];
}
}
int ql,qr,c,k;
struct SegTree
{
ll sumv[MAXN<<2];
int maxv[MAXN<<2],num[MAXN<<2],secv[MAXN<<2];
int minv[MAXN<<2];
#define lson (now<<1)
#define rson (now<<1|1)
void pushup(int now)
{
sumv[now]=sumv[lson]+sumv[rson];
maxv[now]=max(maxv[lson],maxv[rson]);
minv[now]=min(minv[lson],minv[rson]);
if(maxv[lson]==maxv[rson])
{
num[now]=num[lson]+num[rson];
secv[now]=max(secv[lson],secv[rson]);
}
else
{
num[now]=maxv[lson]>maxv[rson]?num[lson]:num[rson];
secv[now]=max(secv[lson],secv[rson]);
secv[now]=max(secv[now],min(maxv[lson],maxv[rson]));
}
}
void build(int now,int l,int r)
{
if(l==r)
{
sumv[now]=cost[idx[l]];
maxv[now]=minv[now]=sumv[now];
num[now]=1;
secv[now]=-1;
return;
}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(now);
}
ll query(int now,int l,int r)
{
if(ql<=l&&r<=qr)
{
if(maxv[now]<c+k)
return 0;
if(secv[now]<c+k)
return (ll)(maxv[now]-c)*num[now];
if(minv[now]>=c+k)
return sumv[now]-(ll)c*(r-l+1);
}
int mid=(l+r)>>1;
ll ans=0;
if(ql<=mid)
ans+=query(lson,l,mid);
if(qr>=mid+1)
ans+=query(rson,mid+1,r);
return ans;
}
}T;
int main()
{
freopen("forging.in","r",stdin);
freopen("forging.out","w",stdout);
int n;
read(n);
for(int i=2;i<=n;++i) // edit 1:root has no father
{
static int fa,w;
read(fa);read(w);
addedge(fa,i,w);
}
dfs(1);
T.build(1,1,n);
int q;
read(q);
while(q--)
{
static int u;
read(u);read(k);
ql=dfn[u],qr=dfn[u]+siz[u]-1,c=cost[u];
printf("%lld\n",T.query(1,1,n));
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
法二:平衡树
题意是给定一棵带边权的有根树,每次询问一个点u的子树内与其距离至少是k的点到u的距离之和。
容易想到如果我们能维护出每个点子树内点到其距离的有序表,询问将十分简单。我们将询问离线挂在每个点上,从下向上用平衡树维护每个点的子树内距离,合并时启发式合并。
\(O(n \log^2 n + q \log n)\)
#include <bits/stdc++.h>
using LL = long long;
const int MAXN = 1e5 + 5;
struct SplayTree {
struct Node {
Node *fa, *ch[2];
LL sum, tag, val;
int size;
Node(LL _val): sum(_val), val(_val) {
fa = ch[0] = ch[1] = 0;
tag = 0;
size = 1;
}
void Add(LL w) {
val += w;
sum += w * size;
tag += w;
}
void Down() {
for (int i = 0; i < 2; ++i) if (ch[i])
ch[i]->Add(tag);
tag = 0;
}
void DownR() {
if (fa) fa->DownR();
Down();
}
void Update() {
sum = val;
size = 1;
for (int i = 0; i < 2; ++i) if (ch[i]) {
sum += ch[i]->sum;
size += ch[i]->size;
}
}
int Which() {
return this == fa->ch[1];
}
} *root;
SplayTree() = default;
void Rotate(Node *k) {
Node *p = k->fa;
int l = k->Which(), r = l ^ 1;
k->fa = p->fa;
if (p->fa) p->fa->ch[p->Which()] = k;
p->ch[l] = k->ch[r];
if (k->ch[r]) k->ch[r]->fa = p;
k->ch[r] = p;
p->fa = k;
p->Update();
k->Update();
}
void Splay(Node *k, Node *aim_fa = 0) {
k->DownR();
while (k->fa != aim_fa) {
Node *p = k->fa;
if (p->fa != aim_fa) {
if (k->Which() ^ p->Which()) Rotate(k);
else Rotate(p);
}
Rotate(k);
}
if (!aim_fa) root = k;
}
void Insert(LL val, Node *&k, Node *fa = 0) {
if (!k) {
k = new Node(val);
k->fa = fa;
Splay(k);
return;
}
k->Down();
Insert(val, k->ch[val > k->val], k);
}
void Add(LL val) {
root->Add(val);
}
void Traverse(std::vector<LL> &vec, Node *k) {
if (!k) return;
k->Down();
Traverse(vec, k->ch[0]);
vec.push_back(k->val);
Traverse(vec, k->ch[1]);
}
void Delete(Node *&k) {
if (!k) return;
for (int i = 0; i < 2; ++i) Delete(k->ch[i]);
delete k; k = 0;
}
void Merge(SplayTree &oth) {
if (root->size < oth.root->size) std::swap(root, oth.root);
// std::cerr << root->size << " " << oth.root->size << std::endl;
std::vector<LL> list;
oth.Traverse(list, oth.root);
oth.Delete(oth.root);
for (LL i: list)
Insert(i, root);
}
LL Query(LL val) {
Node *k = root, *p = 0;
while (k) {
k->Down();
if (k->val < val) {
p = k;
k = k->ch[1];
} else {
k = k->ch[0];
}
}
if (!p) return root->sum;
Splay(p);
if (!root->ch[1]) {
return 0;
} else {
return root->ch[1]->sum;
}
}
} splay[MAXN];
int n, q;
std::vector<int> G[MAXN];
int fa[MAXN], w[MAXN];
std::vector<std::pair<int, int>> qry[MAXN];
LL ans[MAXN];
void DFS(int now) {
splay[now].Insert(0, splay[now].root);
for (int to: G[now]) {
DFS(to);
splay[to].Add(w[to]);
splay[now].Merge(splay[to]);
}
for (auto i: qry[now]) {
int k = i.first, idx = i.second;
ans[idx] = splay[now].Query(k);
}
}
void Main() {
#ifndef LOCAL
freopen("forging.in", "r", stdin);
freopen("forging.out", "w", stdout);
#endif
scanf("%d", &n);
for (int i = 2; i <= n; ++i) {
scanf("%d%d", fa + i, w + i);
G[fa[i]].push_back(i);
}
scanf("%d", &q);
for (int i = 1; i <= q; ++i) {
static int u, k;
scanf("%d%d", &u, &k);
qry[u].emplace_back(k, i);
}
DFS(1);
for (int i = 1; i <= q; ++i)
printf("%lld\n", ans[i]);
}
register char *_sp __asm__("rsp");
int main() {
const int size = 32 << 20;
static char *sys, *mine(new char[size] + size - 4096);
sys = _sp; _sp = mine; Main(); _sp = sys;
return 0;
}