| Time Limit: 4000MS | Memory Limit: 65536K | |
| Total Submissions: 11419 | Accepted: 3140 | 
Description
Input
Output
Sample Input
3 3 1 1 2 1 2 3 2 0 2 1 2 3 0 3
Sample Output
1 3
题目链接:POJ 2763
给定一棵树,两种操作,一种是询问当前点到v点的最短距离,并且输出答案之后当前点就变成v点了;第二种是把某一条边的长度变为w。
先考虑只有询问的情况下怎么做,显然是LCA裸题,设dis[u]代表u点到根节点的距离,则有:$dis(u, v) = dis[u]+dis[v]-2*dis[LCA(u,v)]$,然后考虑第二种操作,可以发现当你改变一条边的长度(权值)的时候,会对这条边下的整颗子树造成影响,比如u——v的一条边,且u比v更靠近根节点的话,那么造成的影响是v所在整颗子树的影响,即子树是深度较深的点所连接的。由于是整颗子树,明显可以用DFS序来转换后用树状数组或者线段树来维护这个DFS序列即dis数组,操作就是区间更新,单点查询——改变边长度的时候显然整个子树序列区间长度均改变了$w‘-w$。好像这题一般写法是树链剖分,蒟蒻我不会……就用树状数组好了,树状数组怎么区间更新?左端点处+val,右端点+1处-val即可。
ZZ地WA了一下午发现是把D数组当作深度数组来用了……
代码:
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define fin(name) freopen(name,"r",stdin)
#define fout(name) freopen(name,"w",stdout)
#define CLR(arr,sum) memset(arr,sum,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 100010;
struct edge
{
	int to, nxt, d;
	edge() {}
	edge(int _to, int _nxt, int _d): to(_to), nxt(_nxt), d(_d) {}
};
edge E[N << 1];
int head[N], tot;
int ver[N << 1], D[N << 1], F[N], sz, dp[N << 1][19];
int W[N], A[N], B[N];
int L[N], R[N], T[N], ts;
bitset<N>vis;
void init()
{
	CLR(head, -1);
	tot = 0;
	sz = 0;
	ts = 0;
	CLR(T, 0);
	vis.reset();
}
void update(int k, int v)
{
	while (k < N)
	{
		T[k] += v;
		k += (k & -k);
	}
}
int query(int k)
{
	int ret = 0;
	while (k)
	{
		ret += T[k];
		k -= (k & -k);
	}
	return ret;
}
inline void add(int s, int t, int d)
{
	E[tot] = edge(t, head[s], d);
	head[s] = tot++;
}
void dfs(int u, int d, int sumdis)
{
	ver[++sz] = u;
	D[sz] = d;
	F[u] = sz;
	L[u] = ++ts;
	update(L[u], sumdis);
	update(L[u] + 1, -sumdis);
	vis[u] = 1;
	for (int i = head[u]; ~i; i = E[i].nxt)
	{
		int v = E[i].to;
		if (!vis[v])
		{
			dfs(v, d + 1, sumdis + E[i].d);
			ver[++sz] = u;
			D[sz] = d;
		}
	}
	R[u] = ts;
}
void RMQinit(int l, int r)
{
	int i, j;
	for (i = l; i <= r; ++i)
		dp[i][0] = i;
	for (j = 1; l + (1 << j) - 1 <= r; ++j)
	{
		for (i = l; i + (1 << j) - 1 <= r; ++i)
		{
			int a = dp[i][j - 1], b = dp[i + (1 << (j - 1))][j - 1];
			dp[i][j] = D[a] < D[b] ? a : b;
		}
	}
}
int LCA(int u, int v)
{
	int l = F[u], r = F[v];
	if (l > r)
		swap(l, r);
	int len = r - l + 1;
	int k = 0;
	while (1 << (k + 1) <= len)
		++k;
	int a = dp[l][k], b = dp[r - (1 << k) + 1][k];
	return D[a] < D[b] ? ver[a] : ver[b];
}
int main(void)
{
	int n, q, s, i, a, b, w;
	while (~scanf("%d%d%d", &n, &q, &s))
	{
		init();
		for (i = 1; i <= n - 1; ++i)
		{
			scanf("%d%d%d", &a, &b, &w);
			A[i] = a;
			B[i] = b;
			W[i] = w;
			add(a, b, w);
			add(b, a, w);
		}
		dfs(s, 1, 0);
		RMQinit(1, sz);
		while (q--)
		{
			int ops;
			scanf("%d", &ops);
			if (ops == 0)
			{
				int v;
				scanf("%d", &v);
				printf("%d\n", query(L[s]) + query(L[v]) - 2 * query(L[LCA(s, v)]));
				s = v;
			}
			else
			{
				int k, neww;
				scanf("%d%d", &k, &neww);
				int u = A[k], v = B[k];
				if (L[u] > L[v])
					swap(u, v);
				update(L[v], neww - W[k]);
				update(R[v] + 1, -(neww - W[k]));
				W[k] = neww;
			}
		}
	}
	return 0;
}
POJ 2763 Housewife Wind(DFS序+LCA+树状数组)
原文:http://www.cnblogs.com/Blackops/p/7142323.html