【BZOJ1103】大都市 解题报告

题目传送门
打算5分钟写完题解

题目大意

有一棵n个点的有根树, 初始时每条边均为红色,有两种操作:

  1. 把某条边染为蓝色
  2. 统计根到某一点路径上的红边数量

思路

  1. \(a_i\)表示根到点i路径上的红边数,修改边(u,v)时,不失一般性,设\(dep(u) < dep(v)\),那么以v为根的子树中,所有\(a_i\)都要减1。用线段树点查区改
  2. 用黑科技欧拉序,不失一般性,设\(dep(u) < dep(v)\),把边(u,v)的颜色下放到点v,线段树/树状数组点改区查即可。

关于欧拉序:算法导论中的DFS序好像就叫欧拉序,它有一个优越的性质:根到任意一点的路径对应欧拉序上的一段前缀区间

易错点

  • 树状数组维护的范围是\([1,2n]\),而不是\([1,n]\)
  • 无向边数组开2倍

代码

#include<iostream>
#include<cstdio>
using namespace std;

const int maxn = 250007;
int n, m, C[maxn << 1];
int stamp, dep[maxn], tid[maxn], tif[maxn];
bool vis[maxn];
int edgenum, head[maxn], Next[maxn << 1], vet[maxn << 1];

inline void addedge(int u, int v){
	++edgenum;
	vet[edgenum] = v;
	Next[edgenum] = head[u];
	head[u] = edgenum;
}

void DFS(int u, int D){
	//printf("%d\n", u);
	vis[u] = true; tid[u] = ++stamp; dep[u] = D;
	for (int e = head[u]; e; e = Next[e]){
		int v = vet[e];
		if (!vis[v]) DFS(v, D+1);
	}
	tif[u] = ++stamp;
}

inline void add(int i, int val){
	for (; i <= n+n; i += (i & (-i)))
		C[i] += val;
}

inline int sum(int i){
	int res = 0;
	for (; i >= 1; i -= (i & (-i)))
		res += C[i];
	return res;
}

int main(){
	scanf("%d", &n);
	for (int i = 1; i < n; ++i){
		int u, v;
		scanf("%d%d", &u, &v);
		addedge(u,v);
		addedge(v,u);
	}
	DFS(1, 1); 
	for (int i = 2; i <= n; ++i){
		add(tid[i], +1);
		add(tif[i], -1); 
	}
	scanf("%d", &m);
	for (int i = 1; i <= n + m - 1; ++i){
		char T[2];
		scanf("%s", T);
		if (T[0] == 'A'){
			int u,v;
			scanf("%d%d", &u, &v);
			if (dep[u] > dep[v]) swap(u, v);
			add(tid[v], -1); add(tif[v], +1);
		}else{
			int a;
			scanf("%d", &a);
			printf("%d\n", sum(tid[a]));
		}
	}
	return 0;
}
posted @ 2018-08-06 20:36  YJZoier  阅读(108)  评论(0编辑  收藏  举报