SPOJ Query on a tree lct裸题

题目链接:点击打开链接

把边权化成点权,每个点的点权表示父边的边权。

求path(x, y)

把x access后,则 x 就变成了根所在的splay , 且x是这条链上深度最大的节点。(下面对于根所在的splay称为splay_root)

那么y沿着父节点爬上去,当父节点 fa_y 坐落在splay_root上时,fa_y深度一定比x小,即一定在x的上方。

再把y access上去, 在y最后要和splay_root合并之前, 把fa_y splay一下,

那么path(x, y) 的一部分就是 fa_y ->x : fa_y->splay(), return paht(fa_y, x) => fa_y->ch[1]->max

另一部分为 y一直access上来的那条链, splay->max

#include <iostream>
#include <fstream>
#include <string>
#include <time.h>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <stack>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
typedef long long ll;
typedef pair<int, int> pii;
const int N = 30005;
const int inf = 10000000;
struct Node *null;
struct Node{
	Node *fa, *ch[2];
	int size;
	int val, ma, sum, id;
	bool rev;
	inline void put(){
		printf("%d:id, %d,%d,%d (%d,%d) fa:%d \n", id, val, ma, sum, ch[0]->id, ch[1]->id, fa->id);
	}
	void debug(Node *x){
		if (x == null)return;
		x->put();
		if (x->ch[0] != null)putchar('L'), debug(x->ch[0]);
		if (x->ch[1] != null)putchar('r'), debug(x->ch[1]);
	}

	inline void clear(int _val, int _id){
		fa = ch[0] = ch[1] = null;
		size = 1;
		rev = 0;
		id = _id;
		val = ma = sum = _val;
	}
	inline void add_val(int _val){
		val += _val;
		sum += _val;
		ma = max(ma, val);
	}
	inline void push_up(){
		size = 1 + ch[0]->size + ch[1]->size;

		sum = ma = val;
		if (ch[0] != null) {
			sum += ch[0]->sum; ma = max(ma, ch[0]->ma);
		}
		if (ch[1] != null){
			sum += ch[1]->sum;
			ma = max(ma, ch[1]->ma);
		}
	}
	inline void push_down(){
		if (rev){
			flip(); ch[0]->rev ^= 1; ch[1]->rev ^= 1;
		}
	}
	inline void setc(Node *p, int d){
		ch[d] = p;
		p->fa = this;
	}
	inline bool d(){
		return fa->ch[1] == this;
	}
	inline bool isroot(){
		return fa == null || fa->ch[0] != this && fa->ch[1] != this;
	}
	inline void flip(){
		if (this == null)return;
		swap(ch[0], ch[1]);
		rev ^= 1;
	}
	inline void go(){//从链头开始更新到this
		if (!isroot())fa->go();
		push_down();
	}
	inline void rot(){
		Node *f = fa, *ff = fa->fa;
		int c = d(), cc = fa->d();
		f->setc(ch[!c], c);
		this->setc(f, !c);
		if (ff->ch[cc] == f)ff->setc(this, cc);
		else this->fa = ff;
		f->push_up();
	}
	inline Node*splay(){
		go();
		while (!isroot()){
			if (!fa->isroot())
				d() == fa->d() ? fa->rot() : rot();
			rot();
		}
		push_up();
		return this;
	}
	inline Node* access(){//access后this就是到根的一条splay,并且this已经是这个splay的根了
		for (Node *p = this, *q = null; p != null; q = p, p = p->fa){
			p->splay()->setc(q, 1);
			p->push_up();
		}
		return splay();
	}
	inline Node* find_root(){
		Node *x;
		for (x = access(); x->push_down(), x->ch[0] != null; x = x->ch[0]);
		return x;
	}
	void make_root(){
		access()->flip();
	}
	void cut(){//把这个点的子树脱离出去
		access();
		ch[0]->fa = null;
		ch[0] = null;
		push_up();
	}
	void cut(Node *x){
		if (this == x || find_root() != x->find_root())return;
		else {
			x->make_root();
			cut();
		}
	}
	void link(Node *x){
		if (find_root() == x->find_root())return;
		else {
			make_root(); fa = x;
		}
	}
};
Node pool[N], *tail;
Node *node[N], *ee[N];
int n, q;
void debug(Node *x){
	if (x == null)return;
	x->put();
	debug(x->ch[0]);
	debug(x->ch[1]);
}
inline int ask(Node *x, Node *y){
	x->access();
	for (x = null; y != null; x = y, y = y->fa){
		y->splay();
		if (y->fa == null)return max(y->ch[1]->ma, x->ma);
		y->setc(x, 1);
		y->push_up();
	}
}
struct Edge{
	int from, to, dis, id, nex;
}edge[N << 1];
int head[N], edgenum;
void add(int u, int v, int dis, int id){
	Edge E = { u, v, dis, id, head[u] };
	edge[edgenum] = E;
	head[u] = edgenum++;
}
bool vis[N];
void bfs(){
	fill(vis + 1, vis + 1 + n, false);
	queue<int>q;
	q.push(1);
	vis[1] = true;
	while (!q.empty()){
		int u = q.front(); q.pop();
		for (int i = head[u]; ~i; i = edge[i].nex){
			int v = edge[i].to;
			if (vis[v])continue;
			vis[v] = true;
			q.push(v);
			ee[edge[i].id] = node[v];
			node[v]->val = edge[i].dis;
			node[v]->push_up();
			node[v]->fa = node[u];
		}
	}
}
int main(){
	int T; rd(T);
	while (T--){
		rd(n);
		fill(head + 1, head + n + 1, -1); edgenum = 0;
		tail = pool;
		null = tail++;

		null->clear(-inf, 0);
		null->size = 0;
		for (int i = 1; i <= n; i++) {
			node[i] = tail++;
			node[i]->clear(0, i);
		}
		for (int i = 1, u, v, d; i < n; i++){
			rd(u); rd(v); rd(d);
			add(u, v, d, i);
			add(v, u, d, i);
		}
		bfs();
		char str[10]; int u, v;
		while (true){
			scanf("%s", str);
			if (str[0] == 'D')break;
			rd(u); rd(v);
			if (str[0] == 'Q')pt(ask(node[u], node[v])), putchar('\n');
			else {
				ee[u]->splay()->val = v;
				ee[u]->push_up();
			}
		}
	}
	return 0;
}
/*
*/

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-17 13:18:29

SPOJ Query on a tree lct裸题的相关文章

SPOJ QTREE3 lct裸题

题目链接 题意: 给定n个点 q个询问 下面n-1行给出树边,点有黑或白色,初始化为白色 下面q行: 询问有2种: 1. 0 x 把x点黑变白,白变黑 2.1 x 询问Path(1,x)路径上第一个黑点的点标, 若不存在黑点则输出-1 思路: lct裸题 #include <iostream> #include <fstream> #include <string> #include <time.h> #include <vector> #inc

SPOJ QTREE2 lct裸题

题目链接 题意: 给一棵树,有边权 1.询问路径的边权和 2.询问沿着路径的第k个点标. 思路:lct裸题. #include <iostream> #include <fstream> #include <string> #include <time.h> #include <vector> #include <map> #include <queue> #include <algorithm> #inclu

spoj 375 query on a tree LCT

这道题是树链剖分的裸题,正在学LCT,用LCT写了,发现LCT代码比树链剖分还短点(但我的LCT跑极限数据用的时间大概是kuangbin大神的树链剖分的1.6倍,所以在spoj上是850ms卡过的). 收获: 1.边转换成点(即若存在边(u,v),则新加一个点z代表边,将z连接u和v,z的点权就是(u,v)的边权,非边点的权设为-oo),然后对边权的统计就变成了对点权的统计(这是LCT中处理边信息的通法之一). 2.若要连接两个点u,v,先让它们分别称为根,然后将其中一个的path-parent

spoj Query on a tree(树链剖分模板题)

375. Query on a tree Problem code: QTREE You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of

【SPOJ Query on a tree 】 (树链剖分)

http://acm.hust.edu.cn/vjudge/problem/13013 题意: 有一棵N个节点的树(1<=N<=10000),N-1条边,边的编号为1~N-1,每条边有一个权值,要求模拟两种操作: 1:QUERY x y 求节点x和节点y之间的路径中权值最大的边. 2:CHANGE p k修改第p条边的权值为k. [分析] 树链剖分裸题.. [表示我一开始怎么TLE,后来怎么AC的并不知道.. 1 #include<cstdio> 2 #include<cst

SPOJ Query on a tree 树链剖分 水题

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of the i-th edge to tior QUERY a b : ask fo

SPOJ QTREE6 lct裸题

题目链接 岛娘出的题,还是比较容易的 #include <iostream> #include <fstream> #include <string> #include <time.h> #include <vector> #include <map> #include <queue> #include <algorithm> #include <stack> #include <cstrin

SPOJ QTREE5 lct裸题

题目链接 对于每个节点,记录这个节点所在链的信息: ls:(链的上端点)距离链内部最近的白点距离 rs:(链的下端点)距离链内部最近的白点距离 注意以上都是实边 虚边的信息用一个set维护. set维护的是对于每个不是链上,但是this的子树,那些子树中距离this最近的白点距离. #include <stdio.h> #include <string.h> #include <set> #include <algorithm> #include <i

一些LCT裸题

又来回炉lct了= = [bzoj3514]: Codechef MARCH14 GERALD07加强版 模版题.常见姿势,把边也当成点. 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=200233<<1; 6 const int inf=1000023333; 7 struct zs{ 8 int u,v