码迷,mamicode.com
首页 > 移动开发 > 详细

【BZOJ】3757 苹果树

时间:2015-03-11 21:35:42      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:

 

题意:n个节点的树,每个点有一种颜色。现有m种询问,每次询问x y a b表示x到y的路径上颜色的种数且a颜色看成b颜色。(n<=50000, m<=100000)

#include <bits/stdc++.h>
using namespace std;
const int N=50005;
int ihead[N], cnt, id[N], blo[N], f[N][16], dep[N], cal[N], col[N], st[N], n, m, Ans[N], ans, ID;
struct E { int next, to; }e[N<<1];
struct Q { int x, y, a, b, id; }q[N*2];
bool cmp(const Q &a, const Q &b) { return blo[a.x]==blo[b.x]?id[a.y]<id[b.y]:blo[a.x]<blo[b.x]; }
void add(int x, int y) { e[++cnt]=(E){ihead[x], y}; ihead[x]=cnt; e[++cnt]=(E){ihead[y], x}; ihead[y]=cnt; }
void dfs(int x) {
	id[x]=++ID;
	for(int i=1; i<=15; ++i) f[x][i]=f[f[x][i-1]][i-1];
	for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f[x][0]) { dep[e[i].to]=dep[x]+1; f[e[i].to][0]=x; dfs(e[i].to); }
}
int LCA(int x, int y) {
	if(dep[x]<dep[y]) swap(x, y);
	int d=dep[x]-dep[y];
	for(int i=15; i>=0; --i) if((d>>i)&1) x=f[x][i]; if(x==y) return x;
	for(int i=15; i>=0; --i) if(f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i];
	return f[x][0];
}
void update(int x) {
	if(st[x]) { if(!(--cal[col[x]])) --ans; }
	else { if(!cal[col[x]]) ++ans; ++cal[col[x]]; }
	st[x]=!st[x];
}
void move(int x, int y) {
	while(x!=y)
		if(dep[x]>dep[y]) update(x), x=f[x][0];
		else update(y), y=f[y][0];
}
bool check(int a, int b) { return cal[a] && cal[b] && a!=b; } //a!=b
void pre() {
	dfs(1);
	for(int i=1; i<=m; ++i) { scanf("%d%d%d%d", &q[i].x, &q[i].y, &q[i].a, &q[i].b), q[i].id=i; if(id[q[i].x]>id[q[i].y]) swap(q[i].x, q[i].y); }
	int sq=sqrt(n+0.5);
	for(int i=1; i<=n; ++i) blo[i]=(i-1)/sq;
	sort(q+1, q+1+m, cmp);
	q[0].x=q[0].y=1;
}
void work() {
	for(int i=1; i<=m; ++i) {
		move(q[i-1].x, q[i].x); move(q[i-1].y, q[i].y);
		int lca=LCA(q[i].x, q[i].y);
		update(lca);
		Ans[q[i].id]=ans;
		if(check(q[i].a, q[i].b)) --Ans[q[i].id];
		update(lca);
	}
	for(int i=1; i<=m; ++i) printf("%d\n", Ans[i]);
}
int main() {
	scanf("%d%d", &n, &m); 
	for(int i=1; i<=n; ++i) scanf("%d", &col[i]);
	for(int i=1; i<=n; ++i) {
		int x, y; scanf("%d%d", &x, &y);
		if(!x || !y) continue;
		add(x, y);
	}
	pre();
	work();
	return 0;
}

  

树上分块= =

首先一开始就强行yy到了dfs序然后分块,后来不知什么鬼我自己不相信这样做能过这题(因为我一开始理解错啦= =假如固定了l,那么r走过的节点数是均摊O(n)的啊= =最多为2n次(进来又出去))

发现题解就是我一开始想的那样= =

可是我忽略了一个问题有木有!在更新lca的时候可能会出问题!自己画图可以得知!反正如果特判的话很难写的样子?

听说vfk神犇有题解我就去膜拜了下= =跪烂

首先设$S(u, v)$表示$u$到$v$路径上的节点集合。令$+$操作表示元素的模2加(即出现次数的模2加法,即异或= =,就是出现两次的就去掉)

容易得到$S(u, v) = S(root, u) + S(root, v) + lca(u, v)$

lca就是最麻烦的,vfk给出了一种巧妙的解法:

设$T(u, v) = S(root, u) + S(root, v)$

则设$u‘$表示$u$要到达的目的地:

$$T(u, v) + T(u‘, v) = S(u, root) + S(u‘, root) + S(v, root) + S(v, root)$$

$$T(u‘, v) = T(u, u‘) + T(u, v)$$

那么我们要从$T(u, v)$转移到$T(u‘, v)$只需要走一次$T(u, u‘)$上的点就行辣!(注意踢掉lca)

而不选lca可以直接用向上爬的方法做= =很简单哒= =

然后计数颜色就开个计数器就行辣= =

然后就没辣= =

 

【BZOJ】3757 苹果树

标签:

原文地址:http://www.cnblogs.com/iwtwiioi/p/4330915.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!