[SDOI2014]旅行

Posted by Dispwnl on January 7, 2018

题目

题目描述

S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。

为了方便,我们用不同的正整数代表各种宗教, S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。

在S国的历史上常会发生以下几种事件:

“CC x c“:城市x的居民全体改信了c教;

“CW x w“:城市x的评级调整为w;

“QS x y“:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和;

“QM x y“:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级最大值。

由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

输入输出格式

输入格式:

输入的第一行包含整数N,Q依次表示城市数和事件数。

接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的评级和信仰。 接下来N-1行每行两个整数x,y表示一条双向道路。

接下来Q行,每行一个操作,格式如上所述。

输出格式:

对每个QS和QM事件,输出一行,表示旅行者记下的数字。

输入输出样例

输入样例#1:

5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4

输出样例#1:

8
9
11
3

说明

N,Q < =10^5 , C < =10^5

数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。

题解

有多少个宗教就建几个线段树,这就得用到动态开点了

几个坑点:

数组要开大一点($1e5\times 32$差不多)

建树时注意树与树之间会重叠

区间查询时只查询与出发点信仰相同的点然而样例能过

如查询$1\rightarrow 3\rightarrow 5$,$5$与$1$信仰相同,$3$不同,所以$3$不查询

还有单点修改时记得储存,不能只把线段树中的点修改,原点也要修改

然后差不多了,耐着性子打完吧

我打了$198$行还慢的要死……

代码

# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm> 
# include<queue>
# include<cmath>
#define mid ((l+r)>>1)
#define tl c[k].l
#define tr c[k].r
#define ini inline int
#define inv inline void
#define ge getchar()
#define is isdigit(ch)
#define cn(a) string a;cin>>a
using namespace std;
const int MAX=1e5+1;
struct p{
    int x,maxn,l,r;
}c[MAX<<5];
struct q{
    int x,y;
}C[MAX<<1];
struct o{
    int deep,id,siz,son,fa,top,w,belief;
}cc[MAX];
int n,m,num,cnt,tot;
int h[MAX],rt[MAX];
ini max(int x,int y)
{
    return x>y?x:y;
}
ini read()
{
    int x=0,f=1;
    char ch=ge;
    while(!is)
    {
        if(ch=='-') f=-1;
        ch=ge;
    }
    while(is)
    {
        x=x*10+ch-48;
        ch=ge;
    }
    return x*f;
}
inv add(int x,int y)
{
    C[++num].x=h[x];
    C[num].y=y;
    h[x]=num;
}
inv pus(int k)
{
    c[k].maxn=max(c[tl].maxn,c[tr].maxn);
    c[k].x=c[tl].x+c[tr].x;
}
void dfs(int x,int f)
{
    cc[x].deep=cc[f].deep+1;
    cc[x].fa=f;
    cc[x].siz=1;
    for(int i=h[x];i;i=C[i].x)
      {
          int y=C[i].y;
          if(y==f) continue;
          dfs(y,x);
          cc[x].siz+=cc[y].siz;
          if(cc[y].siz>cc[cc[x].son].siz)
          cc[x].son=y;
      }
}
void dfs1(int x,int tp)
{
    cc[x].top=tp;
    cc[x].id=++cnt;
    if(cc[x].son) dfs1(cc[x].son,tp);
    for(int i=h[x];i;i=C[i].x)
      {
          int y=C[i].y;
          if(y==cc[x].fa||y==cc[x].son)
          continue;
          dfs1(y,y);
      }
}
void build(int x,int y,int l,int r,int &k)
{
    if(!k) k=++tot;
    if(l==r)
    {
        c[k].maxn=c[k].x=x;
        return;
    }
    if(y<=mid) build(x,y,l,mid,tl);
    else build(x,y,mid+1,r,tr);
    pus(k);
}
void cut(int x,int l,int r,int &k)
{
    if(l==r)
    {
        c[k].maxn=c[k].x=c[k].l=c[k].r=0;
        k=0;
        return;
    }
    if(x<=mid) cut(x,l,mid,tl);
    else cut(x,mid+1,r,tr);
    pus(k);
    if(!tr&&!tl)
    {
        c[k].maxn=c[k].x=c[k].l=c[k].r=0;
        k=0;
    }
}
int ask_max(int l,int r,int k,int L,int R)
{
    if(!k) return 0;
    if(l>=L&&r<=R) return c[k].maxn;
    if(l>R||r<L) return 0;
    return max(ask_max(l,mid,tl,L,R),ask_max(mid+1,r,tr,L,R));
}
int ask_sum(int l,int r,int k,int L,int R)
{
    if(!k) return 0;
    if(l>=L&&r<=R) return c[k].x;
    if(l>R||r<L) return 0;
    return ask_sum(l,mid,tl,L,R)+ask_sum(mid+1,r,tr,L,R);
}
inv CHANGE(int x,int y)
{
    cc[x].w=y;
    build(y,cc[x].id,1,n,rt[cc[x].belief]);
}
inv CUT(int x,int y)
{
    cut(cc[x].id,1,n,rt[cc[x].belief]);
    cc[x].belief=y;
    build(cc[x].w,cc[x].id,1,n,rt[cc[x].belief]);
}
ini ASK_MAX(int x,int y)
{
    int ans=0,RT=cc[x].belief;
    while(cc[x].top!=cc[y].top)
    {
        if(cc[cc[x].top].deep<cc[cc[y].top].deep)
        swap(x,y);
        ans=max(ans,ask_max(1,n,rt[RT],cc[cc[x].top].id,cc[x].id));
        x=cc[cc[x].top].fa;
    }
    if(cc[x].deep>cc[y].deep) swap(x,y);
    ans=max(ans,ask_max(1,n,rt[RT],cc[x].id,cc[y].id));
    return ans;
}
ini ASK_SUM(int x,int y)
{
    int ans=0,RT=cc[x].belief;
    while(cc[x].top!=cc[y].top)
    {
        if(cc[cc[x].top].deep<cc[cc[y].top].deep)
        swap(x,y);
        ans+=ask_sum(1,n,rt[RT],cc[cc[x].top].id,cc[x].id);
        x=cc[cc[x].top].fa;
    }
    if(cc[x].deep>cc[y].deep) swap(x,y);
    ans+=ask_sum(1,n,rt[RT],cc[x].id,cc[y].id);
    return ans;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
      cc[i].w=read(),cc[i].belief=read();
    for(int i=1;i<n;i++)
      {
          int x=read(),y=read();
          add(x,y);
          add(y,x);
      }
    dfs(1,0);
    dfs1(1,1);
    for(int i=1;i<=n;i++)
      build(cc[i].w,cc[i].id,1,n,rt[cc[i].belief]);
    for(int i=1;i<=m;i++)
      {
          cn(a);
          int x=read(),y=read();
          if(a=="CC")
          CUT(x,y);
          if(a=="CW")
          CHANGE(x,y);
          if(a=="QS")
          printf("%d\n",ASK_SUM(x,y));
          if(a=="QM")
          printf("%d\n",ASK_MAX(x,y));
      }
    return 0;
}