「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 }