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


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

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

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

 #include<bits/stdc++.h>
using namespace std;
const int N=1e6+;
int cnt,n,q,tot;
int father[N],seg[N],rev[N<<],son[N],size[N],dep[N],top[N],w[N],c[N];//w评级,c信仰
int head[N<<],next[N<<],go[N<<],root[N<<];
struct Node{
int lson;
int rson;
int maX,toT;
}tree[N<<]; inline int read()
{
int x=,f=;
char ch=getchar();
while(ch<''||ch>'')
{
if(ch=='-') f=-;
ch=getchar();
}
while(ch>=''&&ch<='')
{
x=x*+ch-'';
ch=getchar();
}
return x*f;
} void Add(int from,int to)
{
next[++tot]=head[from];
head[from]=tot;
go[tot]=to;
} void dfs1(int u,int fa)
{
dep[u]=dep[fa]+;
size[u]=;
father[u]=fa;
int e,v;
for(e=head[u];v=go[e],e;e=next[e])
{
if(v==fa) continue;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
} void dfs2(int u)
{
if(son[u])
{
top[son[u]]=top[u];
seg[son[u]]=++seg[];
rev[seg[]]=son[u];
dfs2(son[u]);
}
int e,v;
for(e=head[u];v=go[e],e;e=next[e])
{
if(!top[v])
{
top[v]=v;
seg[v]=++seg[];
rev[seg[]]=v;
dfs2(v);
}
}
} void update(int &rt,int l,int r,int pos,int val)
{
if(!rt) rt=++cnt;
tree[rt].maX=max(val,tree[rt].maX);
tree[rt].toT+=val;
if(l==r) return ;
int mid=l+r>>;
if(mid>=pos) update(tree[rt].lson,l,mid,pos,val);
else update(tree[rt].rson,mid+,r,pos,val);
} void remove(int rt,int l,int r,int pos)
{
if(l==r)
{
tree[rt].maX=;
tree[rt].toT=;
return ;
}
int mid=l+r>>;
if(mid>=pos) remove(tree[rt].lson,l,mid,pos);
else remove(tree[rt].rson,mid+,r,pos);
tree[rt].maX=max(tree[tree[rt].lson].maX,tree[tree[rt].rson].maX);
tree[rt].toT=tree[tree[rt].lson].toT+tree[tree[rt].rson].toT;
} int Qsum(int rt,int l,int r,int x,int y)
{
if(l>y||r<x) return ;
if(l>=x&&r<=y) return tree[rt].toT;
int mid=l+r>>;
return Qsum(tree[rt].lson,l,mid,x,y)+Qsum(tree[rt].rson,mid+,r,x,y);
} int Qmax(int rt,int l,int r,int x,int y)
{
if(l>y||r<x) return ;
if(l>=x&&r<=y) return tree[rt].maX;
int mid=l+r>>;
return max(Qmax(tree[rt].lson,l,mid,x,y),Qmax(tree[rt].rson,mid+,r,x,y));
} int sigsum(int x,int y,int c)
{
int ans=;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=Qsum(root[c],,n,seg[top[x]],seg[x]);
x=father[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=Qsum(root[c],,n,seg[x],seg[y]);
return ans;
} int sigmax(int x,int y,int c)
{
int ans=;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=max(ans,Qmax(root[c],,n,seg[top[x]],seg[x]));
x=father[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans=max(ans,Qmax(root[c],,n,seg[x],seg[y]));
return ans;
} int main()
{
n=read();q=read();
for(int i=;i<=n;i++)
{
w[i]=read();
c[i]=read();
}
int x,y;
for(int i=;i<n;i++)
{
x=read();
y=read();
Add(x,y);
Add(y,x);
}
dfs1(,);
seg[]=seg[]=rev[]=top[]=;
dfs2();
for(int i=;i<=n;i++) update(root[c[i]],,n,seg[i],w[i]);
char str[];
for(int i=;i<=q;i++)
{
scanf("%s",str+);
x=read();y=read();
if(str[]=='C')
{
remove(root[c[x]],,n,seg[x]);
update(root[y],,n,seg[x],w[x]);
c[x]=y;
}
else if(str[]=='W')
{
remove(root[c[x]],,n,seg[x]);
update(root[c[x]],,n,seg[x],y);
w[x]=y;
}
else if(str[]=='S')
{
printf("%d\n",sigsum(x,y,c[x]));
}
else if(str[]=='M')
{
printf("%d\n",sigmax(x,y,c[x]));
}
}
return ;
}

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

顺便贴上大佬的代码:

 #include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 100010
using namespace std;
int n,m,d=,e=,g=;
int c[MAXN],w[MAXN],root[MAXN];
int head[MAXN],id[MAXN],top[MAXN],deep[MAXN],fa[MAXN],son[MAXN],num[MAXN];
struct node1{//结构体前向星
int next,to;
}a[MAXN<<];
struct node2{//动态线段树
int l,r,data1,data2;
}b[MAXN*];
inline int read(){//弱弱的读优
int date=,w=;char c=;
while(c<''||c>''){if(c=='-')w=-;c=getchar();}
while(c>=''&&c<=''){date=date*+c-'';c=getchar();}
return date*w;
}
inline int max(const int &x,const int &y){//手写 max ,感觉有点手残。。。
if(x>y)return x;
return y;
}
void pushup(int rt){//上传
b[rt].data1=b[b[rt].l].data1+b[b[rt].r].data1;
b[rt].data2=max(b[b[rt].l].data2,b[b[rt].r].data2);
}
void pushdown(int rt){//清空
b[rt].data1=b[rt].data2=b[rt].l=b[rt].r=;
}
void insert(int k,int v,int l,int r,int &rt){//插入
int mid;
if(!rt)rt=e++;//如上 第3点
if(l==v&&v==r){
b[rt].data1=b[rt].data2=k;
return;
}
mid=l+r>>;
if(v<=mid)insert(k,v,l,mid,b[rt].l);
else insert(k,v,mid+,r,b[rt].r);
pushup(rt);
}
void remove(int k,int l,int r,int &rt){//删除
int mid;
if(l==r){
pushdown(rt);
rt=;
return;
}
mid=l+r>>;
if(k<=mid)remove(k,l,mid,b[rt].l);
else remove(k,mid+,r,b[rt].r);
pushup(rt);
if(!b[rt].l&&!b[rt].r){//注意这里,左子树 与 右子树 都空时,节点为空
pushdown(rt);
rt=;
}
}
int query1(int s,int t,int l,int r,int rt){//区间求和
if(!rt)return ;//节点为空,返回
int mid;
if(l==s&&r==t)
return b[rt].data1;
mid=l+r>>;
if(t<=mid)return query1(s,t,l,mid,b[rt].l);
else if(s>mid)return query1(s,t,mid+,r,b[rt].r);
else return query1(s,mid,l,mid,b[rt].l)+query1(mid+,t,mid+,r,b[rt].r);
}
int query2(int s,int t,int l,int r,int rt){//区间求最值
if(!rt)return ;
int mid;
if(l==s&&r==t)
return b[rt].data2;
mid=l+r>>;
if(t<=mid)return query2(s,t,l,mid,b[rt].l);
else if(s>mid)return query2(s,t,mid+,r,b[rt].r);
else return max(query2(s,mid,l,mid,b[rt].l),query2(mid+,t,mid+,r,b[rt].r));
}
void add(int x,int y){//加边
a[d].to=y;
a[d].next=head[x];
head[x]=d++;
a[d].to=x;
a[d].next=head[y];
head[y]=d++;
}
void buildtree(int rt){//建树+树剖准备1
int will;
num[rt]=;
for(int i=head[rt];i;i=a[i].next){
will=a[i].to;
if(!deep[will]){
deep[will]=deep[rt]+;
fa[will]=rt;
buildtree(will);
num[rt]+=num[will];
if(num[will]>num[son[rt]])son[rt]=will;
}
}
}
void dfs(int rt,int fa){//树剖准备2
if(son[rt]){
top[son[rt]]=top[rt];
id[son[rt]]=++g;
dfs(son[rt],rt);
}
int v;
for(int i=head[rt];i;i=a[i].next){
v=a[i].to;
if(v==fa||v==son[rt])continue;
top[v]=v;
id[v]=++g;
dfs(v,rt);
}
}
void change1(int x,int y){//修改宗教:原宗教中删除,新宗教中插入
remove(id[x],,n,root[c[x]]);
c[x]=y;
insert(w[x],id[x],,n,root[c[x]]);
}
void change2(int x,int y){//修改评价:直接插入
w[x]=y;
insert(w[x],id[x],,n,root[c[x]]);
}
void work1(int x,int y){//求评价和
int cs=c[x],s=;
while(top[x]!=top[y]){//树剖搞起
if(deep[top[x]]<deep[top[y]])swap(x,y);
s+=query1(id[top[x]],id[x],,n,root[cs]);
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
s+=query1(id[x],id[y],,n,root[cs]);//不要忘了这里。。。
printf("%d\n",s);
}
void work2(int x,int y){//求评价最值
int cs=c[x],s=;
while(top[x]!=top[y]){//同上
if(deep[top[x]]<deep[top[y]])swap(x,y);
s=max(s,query2(id[top[x]],id[x],,n,root[cs]));
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
s=max(s,query2(id[x],id[y],,n,root[cs]));
printf("%d\n",s);
}
int main(){
int x,y;
char ch[];
n=read();m=read();
for(int i=;i<=n;i++){w[i]=read();c[i]=read();}
for(int i=;i<n;i++){
x=read();y=read();
add(x,y);
}
deep[]=id[]=top[]=;//初值
buildtree();
dfs(,);
for(int i=;i<=n;i++)insert(w[i],id[i],,n,root[c[i]]);//建初始线段树
while(m--){//主过程
scanf("%s",ch);x=read();y=read();
if(ch[]=='C'){
if(ch[]=='C')change1(x,y);
if(ch[]=='W')change2(x,y);
}
if(ch[]=='Q'){
if(ch[]=='S')work1(x,y);
if(ch[]=='M')work2(x,y);
}
}
return ;
}
上一篇:P1636 Einstein学画画


下一篇:[转]Ionic系列——CodePen上的优秀Ionic_Demo