「SDOI2014」旅行(信息学奥赛一本通 1564)(洛谷 3313)

题目描述

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。


 

刚看到这道题的时候我还以为很简单,直接用线段树暴力...然鹅这道题相比之前几道题还是有点不太一样,需要将每一个信仰都建一棵树,不然会爆掉...

先贴上我同学肖玉梅(另一个沙雕,点击即可访问杀马特大王的博客)的代码

(别问我为什么补贴自己的代码,还不是因为我自己的到现在还没过)

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=1e6+1;
  4 int cnt,n,q,tot;
  5 int father[N],seg[N],rev[N<<2],son[N],size[N],dep[N],top[N],w[N],c[N];//w评级,c信仰 
  6 int head[N<<1],next[N<<1],go[N<<1],root[N<<2];
  7 struct Node{
  8     int lson;
  9     int rson;
 10     int maX,toT;
 11 }tree[N<<2];
 12 
 13 inline int read()
 14 {
 15     int x=0,f=1;
 16     char ch=getchar();
 17     while(ch<'0'||ch>'9')
 18     {
 19         if(ch=='-') f=-1;
 20         ch=getchar();
 21     }
 22     while(ch>='0'&&ch<='9')
 23     {
 24         x=x*10+ch-'0';
 25         ch=getchar();
 26     }
 27     return x*f;
 28 }
 29 
 30 void Add(int from,int to)
 31 {
 32     next[++tot]=head[from];
 33     head[from]=tot;
 34     go[tot]=to;
 35 }
 36 
 37 void dfs1(int u,int fa)
 38 {
 39     dep[u]=dep[fa]+1;
 40     size[u]=1;
 41     father[u]=fa;
 42     int e,v;
 43     for(e=head[u];v=go[e],e;e=next[e])
 44     {
 45         if(v==fa) continue;
 46         dfs1(v,u);
 47         size[u]+=size[v];
 48         if(size[v]>size[son[u]]) son[u]=v;
 49     }
 50 }
 51 
 52 void dfs2(int u)
 53 {
 54     if(son[u])
 55     {
 56         top[son[u]]=top[u];
 57         seg[son[u]]=++seg[0];
 58         rev[seg[0]]=son[u];
 59         dfs2(son[u]);
 60     }
 61     int e,v;
 62     for(e=head[u];v=go[e],e;e=next[e])
 63     {
 64         if(!top[v])
 65         {
 66             top[v]=v;
 67             seg[v]=++seg[0];
 68             rev[seg[0]]=v;
 69             dfs2(v);
 70         }
 71     }
 72 }
 73 
 74 void update(int &rt,int l,int r,int pos,int val)
 75 {
 76     if(!rt) rt=++cnt;
 77     tree[rt].maX=max(val,tree[rt].maX);
 78     tree[rt].toT+=val;
 79     if(l==r) return ;
 80     int mid=l+r>>1;
 81     if(mid>=pos) update(tree[rt].lson,l,mid,pos,val);
 82     else update(tree[rt].rson,mid+1,r,pos,val);
 83 }
 84 
 85 void remove(int rt,int l,int r,int pos)
 86 {
 87     if(l==r)
 88     {
 89         tree[rt].maX=0;
 90         tree[rt].toT=0;
 91         return ;
 92     }
 93     int mid=l+r>>1;
 94     if(mid>=pos) remove(tree[rt].lson,l,mid,pos);
 95     else remove(tree[rt].rson,mid+1,r,pos);
 96     tree[rt].maX=max(tree[tree[rt].lson].maX,tree[tree[rt].rson].maX);
 97     tree[rt].toT=tree[tree[rt].lson].toT+tree[tree[rt].rson].toT;
 98 }
 99 
100 int Qsum(int rt,int l,int r,int x,int y)
101 {
102     if(l>y||r<x) return 0;
103     if(l>=x&&r<=y) return tree[rt].toT;
104     int mid=l+r>>1;
105     return Qsum(tree[rt].lson,l,mid,x,y)+Qsum(tree[rt].rson,mid+1,r,x,y);
106 }
107 
108 int Qmax(int rt,int l,int r,int x,int y)
109 {
110     if(l>y||r<x) return 0;
111     if(l>=x&&r<=y) return tree[rt].maX;
112     int mid=l+r>>1;
113     return max(Qmax(tree[rt].lson,l,mid,x,y),Qmax(tree[rt].rson,mid+1,r,x,y));
114 }
115 
116 int sigsum(int x,int y,int c)
117 {
118     int ans=0;
119     while(top[x]!=top[y])
120     {
121         if(dep[top[x]]<dep[top[y]]) swap(x,y);
122         ans+=Qsum(root[c],1,n,seg[top[x]],seg[x]);
123         x=father[top[x]];
124     }
125     if(dep[x]>dep[y]) swap(x,y);
126     ans+=Qsum(root[c],1,n,seg[x],seg[y]);
127     return ans;
128 }
129 
130 int sigmax(int x,int y,int c)
131 {
132     int ans=0;
133     while(top[x]!=top[y])
134     {
135         if(dep[top[x]]<dep[top[y]]) swap(x,y);
136         ans=max(ans,Qmax(root[c],1,n,seg[top[x]],seg[x]));
137         x=father[top[x]];
138     }
139     if(dep[x]>dep[y]) swap(x,y);
140     ans=max(ans,Qmax(root[c],1,n,seg[x],seg[y]));
141     return ans;
142 }
143 
144 int main()
145 {
146     n=read();q=read();
147     for(int i=1;i<=n;i++)
148     {
149         w[i]=read();
150         c[i]=read();
151     }
152     int x,y;
153     for(int i=1;i<n;i++)
154     {
155         x=read();
156         y=read();
157         Add(x,y);
158         Add(y,x);
159     }
160     dfs1(1,0);
161     seg[0]=seg[1]=rev[1]=top[1]=1;
162     dfs2(1);
163     for(int i=1;i<=n;i++) update(root[c[i]],1,n,seg[i],w[i]);
164     char str[10];
165     for(int i=1;i<=q;i++)
166     {
167         scanf("%s",str+1);
168         x=read();y=read();
169         if(str[2]=='C')
170         {
171             remove(root[c[x]],1,n,seg[x]);
172             update(root[y],1,n,seg[x],w[x]);
173             c[x]=y;
174         }
175         else if(str[2]=='W')
176         {
177             remove(root[c[x]],1,n,seg[x]);
178             update(root[c[x]],1,n,seg[x],y);
179             w[x]=y;
180         }
181         else if(str[2]=='S')
182         {
183             printf("%d\n",sigsum(x,y,c[x]));
184         }
185         else if(str[2]=='M')
186         {
187             printf("%d\n",sigmax(x,y,c[x]));
188         }
189     }
190     return 0;
191 }

悄咪咪的说一句(别让肖玉梅同学听到了),个人认为洛谷大佬的博客里写的更容易理解一点(从这里可以直接到达并且不需要车票o(* ̄▽ ̄*)ブ

顺便贴上大佬的代码:

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstdio>
  4 #define MAXN 100010
  5 using namespace std;
  6 int n,m,d=1,e=1,g=1;
  7 int c[MAXN],w[MAXN],root[MAXN];
  8 int head[MAXN],id[MAXN],top[MAXN],deep[MAXN],fa[MAXN],son[MAXN],num[MAXN];
  9 struct node1{//结构体前向星
 10     int next,to;
 11 }a[MAXN<<1];
 12 struct node2{//动态线段树
 13     int l,r,data1,data2;
 14 }b[MAXN*20];
 15 inline int read(){//弱弱的读优
 16     int date=0,w=1;char c=0;
 17     while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
 18     while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
 19     return date*w;
 20 }
 21 inline int max(const int &x,const int &y){//手写 max ,感觉有点手残。。。
 22     if(x>y)return x;
 23     return y;
 24 }
 25 void pushup(int rt){//上传
 26     b[rt].data1=b[b[rt].l].data1+b[b[rt].r].data1;
 27     b[rt].data2=max(b[b[rt].l].data2,b[b[rt].r].data2);
 28 }
 29 void pushdown(int rt){//清空
 30     b[rt].data1=b[rt].data2=b[rt].l=b[rt].r=0;
 31 }
 32 void insert(int k,int v,int l,int r,int &rt){//插入
 33     int mid;
 34     if(!rt)rt=e++;//如上 第3点
 35     if(l==v&&v==r){
 36         b[rt].data1=b[rt].data2=k;
 37         return;
 38     }
 39     mid=l+r>>1;
 40     if(v<=mid)insert(k,v,l,mid,b[rt].l);
 41     else insert(k,v,mid+1,r,b[rt].r);
 42     pushup(rt);
 43 }
 44 void remove(int k,int l,int r,int &rt){//删除
 45     int mid;
 46     if(l==r){
 47         pushdown(rt);
 48         rt=0;
 49         return;
 50     }
 51     mid=l+r>>1;
 52     if(k<=mid)remove(k,l,mid,b[rt].l);
 53     else remove(k,mid+1,r,b[rt].r);
 54     pushup(rt);
 55     if(!b[rt].l&&!b[rt].r){//注意这里,左子树 与 右子树 都空时,节点为空
 56         pushdown(rt);
 57         rt=0;
 58     }
 59 }
 60 int query1(int s,int t,int l,int r,int rt){//区间求和
 61     if(!rt)return 0;//节点为空,返回
 62     int mid;
 63     if(l==s&&r==t)
 64     return b[rt].data1;
 65     mid=l+r>>1;
 66     if(t<=mid)return query1(s,t,l,mid,b[rt].l);
 67     else if(s>mid)return query1(s,t,mid+1,r,b[rt].r);
 68     else return query1(s,mid,l,mid,b[rt].l)+query1(mid+1,t,mid+1,r,b[rt].r);
 69 }
 70 int query2(int s,int t,int l,int r,int rt){//区间求最值
 71     if(!rt)return 0;
 72     int mid;
 73     if(l==s&&r==t)
 74     return b[rt].data2;
 75     mid=l+r>>1;
 76     if(t<=mid)return query2(s,t,l,mid,b[rt].l);
 77     else if(s>mid)return query2(s,t,mid+1,r,b[rt].r);
 78     else return max(query2(s,mid,l,mid,b[rt].l),query2(mid+1,t,mid+1,r,b[rt].r));
 79 }
 80 void add(int x,int y){//加边
 81     a[d].to=y;
 82     a[d].next=head[x];
 83     head[x]=d++;
 84     a[d].to=x;
 85     a[d].next=head[y];
 86     head[y]=d++;
 87 }
 88 void buildtree(int rt){//建树+树剖准备1
 89     int will;
 90     num[rt]=1;
 91     for(int i=head[rt];i;i=a[i].next){
 92         will=a[i].to;
 93         if(!deep[will]){
 94             deep[will]=deep[rt]+1;
 95             fa[will]=rt;
 96             buildtree(will);
 97             num[rt]+=num[will];
 98             if(num[will]>num[son[rt]])son[rt]=will;
 99         }
100     }
101 }
102 void dfs(int rt,int fa){//树剖准备2
103     if(son[rt]){
104         top[son[rt]]=top[rt];
105         id[son[rt]]=++g;
106         dfs(son[rt],rt);
107     }
108     int v;
109     for(int i=head[rt];i;i=a[i].next){
110         v=a[i].to;
111         if(v==fa||v==son[rt])continue;
112         top[v]=v;
113         id[v]=++g;
114         dfs(v,rt);
115     }
116 }
117 void change1(int x,int y){//修改宗教:原宗教中删除,新宗教中插入
118     remove(id[x],1,n,root[c[x]]);
119     c[x]=y;
120     insert(w[x],id[x],1,n,root[c[x]]);
121 }
122 void change2(int x,int y){//修改评价:直接插入
123     w[x]=y;
124     insert(w[x],id[x],1,n,root[c[x]]);
125 }
126 void work1(int x,int y){//求评价和
127     int cs=c[x],s=0;
128     while(top[x]!=top[y]){//树剖搞起
129         if(deep[top[x]]<deep[top[y]])swap(x,y);
130         s+=query1(id[top[x]],id[x],1,n,root[cs]);
131         x=fa[top[x]];
132     }
133     if(deep[x]>deep[y])swap(x,y);
134     s+=query1(id[x],id[y],1,n,root[cs]);//不要忘了这里。。。
135     printf("%d\n",s);
136 }
137 void work2(int x,int y){//求评价最值
138     int cs=c[x],s=0;
139     while(top[x]!=top[y]){//同上
140         if(deep[top[x]]<deep[top[y]])swap(x,y);
141         s=max(s,query2(id[top[x]],id[x],1,n,root[cs]));
142         x=fa[top[x]];
143     }
144     if(deep[x]>deep[y])swap(x,y);
145     s=max(s,query2(id[x],id[y],1,n,root[cs]));
146     printf("%d\n",s);
147 }
148 int main(){
149     int x,y;
150     char ch[3];
151     n=read();m=read();
152     for(int i=1;i<=n;i++){w[i]=read();c[i]=read();}
153     for(int i=1;i<n;i++){
154         x=read();y=read();
155         add(x,y);
156     }
157     deep[1]=id[1]=top[1]=1;//初值
158     buildtree(1);
159     dfs(1,0);
160     for(int i=1;i<=n;i++)insert(w[i],id[i],1,n,root[c[i]]);//建初始线段树
161     while(m--){//主过程
162         scanf("%s",ch);x=read();y=read();
163         if(ch[0]=='C'){
164             if(ch[1]=='C')change1(x,y);
165             if(ch[1]=='W')change2(x,y);
166         }
167         if(ch[0]=='Q'){
168             if(ch[1]=='S')work1(x,y);
169             if(ch[1]=='M')work2(x,y);
170         }
171     }
172     return 0;
173 }

 

转载于:https://www.cnblogs.com/ljy-endl/p/11375826.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值