洛谷传送门
BZOJ传送门
题目描述
奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有 名情报员。每名情报员可能有若干名 (可能没有) 下线,除 名大头目外其余 名情报员有且仅有 名上线。奈特公司纪律森严,每名情报员只能与自己的上、下线联系,同时,情报网络中任意两名情报员一定能够通过情报网络传递情报。
奈特公司每天会派发以下两种任务中的一个任务:
- 搜集情报:指派 号情报员搜集情报;
- 传递情报:将一条情报从 号情报员传递给 号情报员。
情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为 ;一旦某个情报员开始搜集情报,他的危险值就会持续增加,每天增加 点危险值 (开始搜集情报的当天危险值仍为 ,第 天危险值为 ,第 天危险值为 ,以此类推)。传递情报并不会使情报员的危险值增加。
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值 。奈特公司认为,参与传递这条情报的所有情报员中,危险值大于 的情报员将对该条情报构成威胁。现在,奈特公司希望知道,对于每个传递情报任务,参与传递的情报员有多少个,其中对该条情报构成威胁的情报员有多少个。
输入输出格式
输入格式:
第一行包含一个正整数 ,表示情报员个数。 笫二行包含 个非负整数,其中第 个整数 表示 号情报员上线的编号。特别地,若 ,表示 号情报员是大头目。 第三行包含一个正整数 ,表示奈特公司将派发 个任务 (每天一个)。
随后 行,依次描述 个任务。每行首先有一个正整数 。
- 若 ,表示任务是传递情报,随后有三个正整数 、 、 ,依次表示传递情报的起点、终点和风险控制值。
- 若 ,表示任务是搜集情报,随后有 个正整数 ,表示搜集情报的情报员编号。
输出格式:
对于每个传递情报任务输出一行,包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数。
输入输出样例
输入样例#1:
7
0 1 1 2 2 3 3
6
1 4 7 0
2 1
2 4
2 7
1 4 7 1
1 4 7 3
输出样例#1:
5 0
5 2
5 1
说明
样例解释:
对于 个传递情报任务,都是经过 名情报员,分别是 号、 号、 号、 号和 号。
- 第 个任务,所有情报员 (危险值为 ) 都不对情报构成威胁;
- 第 个任务,有 名情报员对情报构成威胁,分别是 号情报员 (危险值为 ) 和 号情报员 (危险值为 ), 号情报员 (危险值为 ) 并不构成威胁;
- 第 个任务,只有 名情报员对情报构成威胁。
数据范围:
解题分析
显然对于一个询问 及之前的时刻路径上的情报员没有开始收集情报即可, 所以我们把修改和查询全部离线下来按时间排序, 依次用树剖处理即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 200500
#define ls tree[now].son[0]
#define rs tree[now].son[1]
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int dot, q, dcnt, cnt, root, qcnt;
int dfn[MX], head[MX], dep[MX], fat[MX], son[MX], siz[MX], topf[MX], ans1[MX], ans2[MX];
struct Edge {int to, nex;} edge[MX << 1];
struct Node {int son[2], sum;} tree[MX << 2];
struct OPT {int typ, lef, rig, tim, id;}eve[MX];
IN bool operator < (const OPT &x, const OPT &y)
{return x.tim == y.tim ? x.typ < y.typ : x.tim < y.tim;}
IN void add(R int from, R int to) {edge[++cnt] = {to, head[from]}, head[from] = cnt;}
void DFS(R int now)
{
siz[now] = 1;
for (R int i = head[now]; i; i = edge[i].nex)
{
dep[edge[i].to] = dep[now] + 1;
DFS(edge[i].to);
siz[now] += siz[edge[i].to];
if(siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to;
}
}
void DFS(R int now, R int grand)
{
dfn[now] = ++dcnt;
topf[now] = grand;
if(!son[now]) return;
DFS(son[now], grand);
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == son[now]) continue;
DFS(edge[i].to, edge[i].to);
}
}
void modify(int &now, R int lef, R int rig, R int tar)
{
if(!now) now = ++cnt;
tree[now].sum++;
if(lef == rig) return;
int mid = lef + rig >> 1;
if(tar <= mid) modify(ls, lef, mid, tar);
else modify(rs, mid + 1, rig, tar);
}
int query(R int now, R int lef, R int rig, R int lb, R int rb)
{
if(!now) return 0;
if(lef >= lb && rig <= rb) return tree[now].sum;
int mid = lef + rig >> 1, ret = 0;
if(lb <= mid) ret += query(ls, lef, mid, lb, rb);
if(rb > mid) ret += query(rs, mid + 1, rig, lb, rb);
return ret;
}
void work(R int i)
{
if(eve[i].typ == 2) modify(root, 1, dot, dfn[eve[i].lef]);
else
{
R int x = eve[i].lef, y = eve[i].rig, ans = 0, dp = dep[x] + dep[y];
W (topf[x] ^ topf[y])
{
if(dep[topf[x]] < dep[topf[y]]) std::swap(x, y);
ans += query(root, 1, dot, dfn[topf[x]], dfn[x]);
x = fat[topf[x]];
}
if(dep[x] < dep[y]) std::swap(x, y);
ans += query(root, 1, dot, dfn[y], dfn[x]);
dp -= dep[y] << 1;
ans1[eve[i].id] = ans, ans2[eve[i].id] = dp;
}
}
int main(void)
{
in(dot);
for (R int i = 1; i <= dot; ++i) in(fat[i]), add(fat[i], i);
in(q);
for (R int i = 1; i <= q; ++i)
{
in(eve[i].typ);
if(eve[i].typ == 1)
{
++qcnt;
in(eve[i].lef), in(eve[i].rig), in(eve[i].tim);
eve[i].tim = i - eve[i].tim; eve[i].id = qcnt;
}
else in(eve[i].lef), eve[i].tim = i;
}
std::sort(eve + 1, eve + 1 + q);
DFS(1), DFS(1, 1);
for (R int i = 1; i <= q; ++i) work(i);
for (R int i = 1; i <= qcnt; ++i) printf("%d %d\n", ans2[i] + 1, ans1[i]);
}