【题目描述】
奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有n名情报员。每名情报员可能有若干名(可能没有)下线,除1名大头日外其余n-1名情报员有且仅有1名上线。奈特公司纪律森严,每名情报员只能与自己的上、下线联系,同时,情报网络中仟意两名情报员一定能够通过情报网络传递情报。
奈特公司每天会派发以下两种任务中的一个任务: 1.搜集情报:指派T号情报员搜集情报 2.传递情报:将一条情报从X号情报员传递给Y号情报员
情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为0;-旦某个情报员开始搜集情报,他的危险值就会持续增加,每天增加1点危险值(开始搜集情报的当天危险值仍为0,第2天危险值为1,第3天危险值为2,以此类推)。传递情报并不会使情报员的危险值增加。
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值C。余特公司认为,参与传递这条情报的所有情报员中,危险值大于C的情报员将对该条情报构成威胁。现在,奈特公司希望知道,对于每个传递情报任务,参与传递的情报员有多少个,其中对该条情报构成威胁的情报员有多少个。
【题目链接】
BZOJ 4448 情报传递 SCOI2015
【解题思路】
首先贴出一位神犇的一行题解:
首先,一个间谍在T时刻危险值大于C等价于这个间谍在T-C时刻之前开始搜集情报,于是操作就变成了单点修改和历史版本链求和,可以轻松地转成子树修改和历史版本单点查询,再用DFS序转成区间修改和历史版本单点查询。使用可持久化线段树即可。
思路很明确也很直观,关键就在于 ”一个间谍在T时刻危险值大于C等价于这个间谍在T-C时刻之前开始搜集情报“ 这一个转化。
什么,你不会可持久化线段树?恩...我也还不会。 那怎么办呢?
注意到题目并没有强制在线(稀有的不强制在线的数据结构题),所以我们可以离线处理。
把所有操作按时间排序,依次处理,即相当于在恰当的时间执行修改操作,避免了对历史版本的需求。
回来会用可持久化线段树补上此题的,这个是不能偷懒滴(>_<)。
那么现在就先树链剖分加离线处理吧。
【AC代码】
#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#define MAXN 1000000
#define MAXTIME 1000000
struct Node;
struct Path;
struct Node{
Node *father;
Node *children, *next;
bool asked;
int depth, size;
Node *maxChild;
int maxDepth, pos;
Path *path;
Node(){
father = children = next = maxChild = NULL;
path = NULL;
asked = false;
depth = size = maxDepth = 0;
}
} vs[MAXN];
int n;
Node *root;
inline void addChild(int u, int v){
(vs + v)->next = (vs + u)->children;
(vs + v)->father = vs + u;
(vs + u)->children = vs + v;
}
#define L 0
#define R 1
#define mid (this->l + this->r >> 1)
struct SegmentTree{
int l, r; // [l, r)
SegmentTree* child[2];
int count;
SegmentTree(int l, int r){
this->l = l, this->r = r;
if(r - l == 1){
child[L] = child[R] = NULL;
count = 0;
}
else{
child[L] = new SegmentTree(l, mid);
child[R] = new SegmentTree(mid, r);
update();
}
}
void update(){
count = 0;
if(child[L]) count += child[L]->count;
if(child[R]) count += child[R]->count;
}
void change(int pos){
if(r - l == 1) count = 1;
else child[mid <= pos]->change(pos), update();
}
int query(int l, int r){
// [l, r)
if(l == this->l && this->r == r) return count;
else return
(l < mid ? child[L]->query(l, std::min(mid, r)) : 0) +
(r > mid ? child[R]->query(std::max(l, mid), r) : 0) ;
}
};
struct Path{
SegmentTree *S;
Node *top;
Path(Node *v){
top = v;
S = new SegmentTree(0, v->maxDepth - v->depth + 1);
}
};
inline void cut(){
std::stack<Node*> S;
for(Node *v = vs + 1; v != vs + n + 1; v++) v->asked = false;
root->depth = 0;
S.push(root);
while(!S.empty()){
Node *v = S.top();
if(!v->asked){
for(Node *vi = v->children; vi; vi = vi->next){
vi->depth = v->depth + 1;
S.push(vi);
}
v->asked = true;
}
else{
v->size = 1;
for(Node *vi = v->children; vi; vi = vi->next){
v->size += vi->size;
if(!v->maxChild || vi->size > v->maxChild->size){
v->maxChild = vi;
}
}
if(v->maxChild) v->maxDepth = v->maxChild->maxDepth;
else v->maxDepth = v->depth;
S.pop();
}
}
std::queue<Node*> Q;
Q.push(root);
while(!Q.empty()){
Node *v = Q.front(); Q.pop();
if(v == root || v != v->father->maxChild) v->path = new Path(v), v->pos = 0;
else v->path = v->father->path, v->pos = v->father->pos + 1;
for(Node *vi = v->children; vi; vi = vi->next) Q.push(vi);
}
}
inline void query(int u, int v, int &dist, int &num){
dist = num = 0;
Node *a = vs + u, *b = vs + v;
while(a->path != b->path){
if(a->path->top->depth < b->path->top->depth) std::swap(a, b);
num += a->path->S->query(0, a->pos + 1);
dist += a->depth - a->path->top->father->depth;
a = a->path->top->father;
}
if(a->pos > b->pos) std::swap(a, b);
num += a->path->S->query(a->pos, b->pos + 1);
dist += b->pos - a->pos + 1;
}
inline void change(int u){
Node *v = vs + u;
v->path->S->change(v->pos);
}
enum Type{
Query, Change
};
struct Request{
int id;
int T;
Type type;
// For Query:
int u, v;
int dist, num;
// For Change:
int x;
} rs[MAXTIME];
bool compTime(const Request &a, const Request &b){
return a.T < b.T || (a.T == b.T && a.type == Query && b.type == Change);
}
bool compId(const Request &a, const Request &b){
return a.id < b.id;
}
int main(){
int n, m;
scanf("%d", &n);
for(int vi = 1; vi <= n; vi++){
int v;
scanf("%d", &v);
if(v) addChild(v, vi);
else root = vs + vi;
}
scanf("%d", &m);
for(int i = 0; i < m; i++){
Request *r = rs + i;
int opt;
scanf("%d", &opt);
if(opt == 1){
int c;
scanf("%d%d%d", &r->u, &r->v, &c);
r->id = i, r->T = i - c, r->type = Query;
}
else{
scanf("%d", &r->x);
r->id = r->T = i;
r->type = Change;
}
}
cut();
std::sort(rs, rs + m, compTime);
for(Request *r = rs; r != rs + m; r++){
if(r->type == Query) query(r->u, r->v, r->dist, r->num);
else change(r->x);
}
std::sort(rs, rs + m, compId);
for(Request *r = rs; r != rs + m; r++){
if(r->type == Query) printf("%d %d\n", r->dist, r->num);
}
return 0;
}