题目大意:对一棵有n(n<=10000)个点的树若干次操作,每次操作可以把第i条边的权值改为w,或者求u节点到v节点的路径上所有边的最大权值。
解题思路:对这棵树进行树链剖分,然后用线段树维护区间最大值。
树链剖分理解:通过规定一种特殊的次序,将树形结构变为线性结构,从而可以用多种数据结构来解决树上路径方面的问题。
AC代码:
/*
@Author: wchhlbt
@Date: 2017/7/22
*/
#include <bits/stdc++.h>
#define Fori(x) for(int i=0;i<x;i++)
#define Forj(x) for(int j=0;j<x;j++)
#define maxn 10005
#define inf 0x3f3f3f3f
#define ONES(x) __builtin_popcount(x)
#define pb push_back
#define _ << " " <<
using namespace std;
typedef long long ll ;
const double eps =1e-8;
const int mod = 1000000007;
typedef pair<int, int> P;
const double PI = acos(-1.0);
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int n,m;
int ans;
int cnt;
struct edge{
int u,v,val;
void read(){
scanf("%d%d%d",&u,&v,&val);
}
}edge[maxn];
vector<int> e[maxn];
/*树链剖分
cnt初始化为0
son数组初始化为-1
getans()根据实际维护的值进行修改
*/
int son[maxn];//重儿子
int size[maxn];//子数大小
int fa[maxn];//父节点
int dep[maxn];//深度
void dfs1(int u, int f, int d)//维护重儿子,深度,父节点,子树大小
{
size[u] = 1;//初始化size
fa[u] = f;//标注父节点
dep[u] = d;
son[u] = 0;//初始化重儿子编号为0
for(int i = 0; i<e[u].size(); i++){
int v = e[u][i];
if(v==f) continue;
dfs1(v,u,d+1);
size[u] += size[v];
if(size[v]>size[son[u]])
son[u] = v;
}
}
int id[maxn];
int top[maxn];
//dfs2建立重链
void dfs2(int u, int tp)//tp为当前重链的起始点
{
top[u] = tp;
id[u] = ++cnt;
if(son[u]!=-1) dfs2(son[u],tp);
for(int i = 0; i<e[u].size(); i++){
int v = e[u][i];
if(v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
}
//线段树
int val[maxn];
//区间最大值线段树 支持单点修改
struct NODE
{
int l,r,val;
} node[4*maxn];
int build(int root, int l, int r)
{
node[root].l = l;
node[root].r = r;
if(l==r)
return node[root].val = val[l];
int mid = (l+r)>>1;
int a = build(root<<1, l, mid);
int b = build(root<<1 | 1 ,mid+1, r);
return node[root].val = max(a,b);
}
int update(int root, int pos, int val)//将pos位置的值替换为val
{
if (pos < node[root].l || pos > node[root].r)
return node[root].val;
if (node[root].l == pos && node[root].r == pos)
return node[root].val = val;
int a = update (root<<1, pos, val);
int b = update (root<<1 | 1 , pos, val);
node[root].val = max(a,b);
return node[root].val;
}
int query(int root, int l, int r)
{
if(l>node[root].r || r<node[root].l)//无交集
return 0;
if(l<=node[root].l && node[root].r<=r)//此区间包含root所管理的区间
return node[root].val;
int a = query(root<<1, l, r);//部分相交
int b = query(root<<1 | 1 ,l, r);
return max(a,b);
}
/*****************************************/
int getans(int u, int v)
{
int tp1 = top[u]; int tp2 = top[v];
int ans = 0;
while(tp1!=tp2){
if(dep[tp2]>dep[tp1]){
swap(u,v);
swap(tp1,tp2);
}
int temp = query(1,id[tp1],id[u]);//考虑tp1上面的轻链
ans = max(ans,temp);
u = fa[tp1]; tp1 = top[u];//更新u节点为重链头节点的父节点
}
if(u==v) return ans;
//处理u、v在一条重链上的情况
if(dep[v]>dep[u]) swap(u,v),swap(tp1,tp2);
int temp = query(1,id[son[v]],id[u]);
ans = max(ans,temp);
return ans;
}
void init()//全部初始化操作
{
cnt = 0;//重新编号树上的id
memset(son,-1,sizeof(son));
for(int i = 0; i<maxn-2; i++)
e[i].clear();
}
int main()
{
//freopen("test.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--){
init();
scanf("%d",&n);
for(int i = 1; i<n; i++){
edge[i].read();
e[edge[i].u].pb(edge[i].v);
e[edge[i].v].pb(edge[i].u);
}
dfs1(1,0,1);
dfs2(1,1);
for (int i = 1; i < n; i++) {//将u节点调整成更深的节点
if (dep[edge[i].u] < dep[edge[i].v]) swap(edge[i].u, edge[i].v);
val[id[edge[i].u]] = edge[i].val;
}
build(1,1,cnt);
char s[200];
while(~scanf("%s",&s) && s[0]!='D'){
int x,y;
scanf("%d%d",&x,&y);
if(s[0]=='Q')
printf("%d\n",getans(x,y));
else
update(1,id[edge[x].u],y);//单点更新
}
}
return 0;
}
/*
unsigned int 0~4294967295
int 2147483648~2147483647
unsigned long 0~4294967295
long 2147483648~2147483647
long long的最大值:9223372036854775807
long long的最小值:-9223372036854775808
unsigned long long的最大值:18446744073709551615
__int64的最大值:9223372036854775807
__int64的最小值:-9223372036854775808
unsigned __int64的最大值:18446744073709551615
*/