题目链接 luogu - P3313
题意:中文题
思路:用树链剖分处理图后,可对每一个宗教用一颗线段树维护其区间内的和以及最大值,由于内存有限,故使用动态开点从而节省空间(本来花了大概一小时就完成了,结果把区间查询写成了单点查询,花了一个小时才改出来。。。)
#include
#include
using namespace std;
const int N = 1e5 + 10, MAXN = 3e6;
struct edge
{
int to, next;
}Edge[N<<1];
int cnt, head[N], bel[N], qua[N]; //bel[]储存每个城市的信仰,qua[]储存每个城市的评级
int f[N], son[N], num[N], dep[N], id[N], top[N];
int R[N], lc[MAXN], rc[MAXN], sum[MAXN], MAX[MAXN];
inline int max(int a,int b){ return a > b ? a : b; }
inline int read()
{
char c;
int ans = 0;
c = getchar();
while(!('0' <= c && c <= '9')) c = getchar();
while('0' <= c && c <= '9'){
ans = ans * 10 + c - '0';
c = getchar();
}
return ans;
}
inline void add_edge(int from,int to)
{
Edge[++cnt].to = to;
Edge[cnt].next = head[from];
head[from] = cnt;
}
void dfs1(int v)
{
num[v] = 1;
dep[v] = dep[f[v]] + 1;
for(int i = head[v]; i; i = Edge[i].next){
int to = Edge[i].to;
if(to == f[v]) continue;
f[to] = v;
dfs1(to);
num[v] += num[to];
if(num[to] > num[son[v]])
son[v] = to;
}
}
void dfs2(int v,int tp)
{
top[v] = tp;
id[v] = ++cnt;
if(!son[v]) return ;
dfs2(son[v],tp);
for(int i = head[v]; i; i = Edge[i].next){
int to = Edge[i].to;
if(to == son[v] || to == f[v]) continue;
dfs2(to,to);
}
}
void update(int L,int C,int l,int r,int &rt)
{
if(!rt) rt = ++cnt;
if(l == r){
sum[rt] = MAX[rt] = C;
return ;
}
int m = (l+r)>>1;
if(L <= m) update(L,C,l,m,lc[rt]);
else update(L,C,m+1,r,rc[rt]);
sum[rt] = sum[lc[rt]] + sum[rc[rt]];
MAX[rt] = max(MAX[lc[rt]], MAX[rc[rt]]);
}
void erase(int L,int l,int r,int &rt)
{
if(l == r){
sum[rt] = MAX[rt] = 0;
return ;
}
int m = (l+r)>>1;
if(L <= m) erase(L,l,m,lc[rt]);
else erase(L,m+1,r,rc[rt]);
sum[rt] = sum[lc[rt]] + sum[rc[rt]];
MAX[rt] = max(MAX[lc[rt]], MAX[rc[rt]]);
}
int queryMAX(int L,int R,int l,int r,int rt)
{
if(!rt) return 0;
if(L <= l && r <= R)
return MAX[rt];
int m = (l+r)>>1, ans = 0;
if(L <= m) ans = max(ans, queryMAX(L,R,l,m,lc[rt]));
if(m < R) ans = max(ans, queryMAX(L,R,m+1,r,rc[rt]));
return ans;
}
int querySUM(int L,int R,int l,int r,int rt)
{
if(!rt) return 0;
if(L <= l && r <= R)
return sum[rt];
int m = (l+r)>>1, ans = 0;
if(L <= m) ans += querySUM(L,R,l,m,lc[rt]);
if(m < R) ans += querySUM(L,R,m+1,r,rc[rt]);
return ans;
}
void queryMAXs(int A,int B)
{
int ans = 0, r = bel[A];
while(top[A] != top[B]){
if(dep[top[A]] < dep[top[B]]) swap(A,B);
ans = max(ans, queryMAX(id[top[A]],id[A],1,1e5,R[r]));
A = f[top[A]];
}
if(id[A] > id[B]) swap(A,B);
printf("%d\n", max(ans, queryMAX(id[A],id[B],1,1e5,R[r])));
}
void querySUMs(int A,int B)
{
int ans = 0, r = bel[A];
while(top[A] != top[B]){
if(dep[top[A]] < dep[top[B]]) swap(A,B);
ans += querySUM(id[top[A]],id[A],1,1e5,R[r]);
A = f[top[A]];
}
if(id[A] > id[B]) swap(A,B);
printf("%d\n", ans + querySUM(id[A],id[B],1,1e5,R[r]));
}
int main()
{
char oper[2];
int n, m, A, B;
n = read(); m = read();
for(int i = 1; i <= n; ++i){
A = read(); B = read();
bel[i] = B, qua[i] = A;
}
for(int i = 0; i < n - 1; ++i){
A = read(); B = read();
add_edge(A,B);
add_edge(B,A);
}
cnt = 0; dfs1(1); dfs2(1,1); cnt = 0;
for(int i = 1; i <= n; ++i){
update(id[i],qua[i],1,1e5,R[bel[i]]);
}
while(m--){
scanf("%s",oper); A = read(); B = read();
if(oper[1] == 'S')
querySUMs(A,B);
else if(oper[1] == 'M')
queryMAXs(A,B);
else if(oper[1] == 'C'){
erase(id[A],1,1e5,R[bel[A]]); //将原来宗教在该城市的值去除
bel[A] = B;
update(id[A],qua[A],1,1e5,R[B]);
}
else{
update(id[A],B,1,1e5,R[bel[A]]);
qua[A] = B;
}
}
return 0;
}