题目描述

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

「SDOI2014」旅行(信息学奥赛一本通 1564)(洛谷 3313)的更多相关文章

  1. 「SDOI2014」重建 解题报告

    「SDOI2014」重建 题意 给一个图\(G\),两点\((u,v)\)有边的概率是\(p_{u,v}\),求有\(n-1\)条边通行且组成了一颗树的概率是多少. 抄了几个矩阵树定理有趣的感性说法 ...

  2. 「SDOI2014」Lis 解题报告

    「SDOI2014」Lis 题目描述 给定序列 \(A\),序列中的每一项 \(A_i\) 有删除代价 \(B_i\) 和附加属性 \(C_i\). 请删除若干项,使得 \(A\) 的最长上升子序列长 ...

  3. 「SDOI2014」数数 解题报告

    「SDOI2014」数数 题目描述 我们称一个正整数 \(N\) 是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(S\) 中任意一个元素作为其子串. 例如当 \(S=(\)22, 333, 0 ...

  4. 「SDOI2014」向量集 解题报告

    「SDOI2014」向量集 维护一个向量集合,在线支持以下操作: A x y :加入向量 \((x, y)\): Q x y l r:询问第 \(L\) 个到第 \(R\) 个加入的向量与向量 \(( ...

  5. $ybt\ 【信息学奥赛一本通】题解目录$

    [信息学奥赛一本通]题解目录 $ \large -> OJ$ $ problem1000 $ \(Answer\) - > $ \large 1000$ $ problem1001 $ \ ...

  6. 「JSOI2013」旅行时的困惑

    「JSOI2013」旅行时的困惑 传送门 由于我们的图不仅是一个 \(\text{DAG}\) 而且在形态上还是一棵树,也就是说我们为了实现节点之间互相可达,就必须把每条边都覆盖一次,因为两个点之间的 ...

  7. 「JSOI2010」旅行

    「JSOI2010」旅行 传送门 比较妙的一道 \(\text{DP}\) 题,思维瓶颈应该就是如何确定状态. 首先将边按边权排序. 如果我们用 \(01\) 串来表示 \(m\) 条边是否在路径上, ...

  8. 2019寒假练题计划——LibreOJ刷题计划 &《信息学奥赛一本通》提高版题目

    目录 2019.1.27 #10082. 「一本通 3.3 例 1」Word Rings 题意 思路 #10083. 「一本通 3.3 例 2」双调路径 题意 思路 #10084. 「一本通 3.3 ...

  9. 【09NOIP提高组】Hankson 的趣味题(信息学奥赛一本通 1856)(洛谷 1072)

    题目描述 Hanks 博士是BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫Hankson.现在,刚刚放学回家的Hankson 正在思考一个有趣的问题.今天在课堂上,老师讲解了如何求 ...

随机推荐

  1. C#多线程解决程序卡顿问题

    描述: 在 C# 中,System.Threading.Thread 类用于线程的工作.它允许创建并访问多线程应用程序中的单个线程.进程中第一个被执行的线程称为主线程. 案例: static void ...

  2. Quartz.net使用笔记

    一.需求场景:每天固定时间执行某个行为/动作. 一开始想用定时器,后来无意间发现了这个插件,感觉功能太强大了,完美解决了我的问题. 二.下载地址:https://www.quartz-schedule ...

  3. 服务发现对比:Zookeeper vs etcd vs Consul

    我们拥有的服务越多,如果我们使用预定义的端口,就会发生冲突的可能性越大.毕竟,在同一端口上不能监听两个服务.管理一百个服务所使用的所有端口的紧密列表本身就是一项挑战.将那些服务所需的数据库添加到该列表 ...

  4. DevExpress之GridControl控件小知识

    DevExpress之GridControl控件小知识 一.当代码中的DataTable中有建数据关系时,DevExpress 的 GridControl 会自动增加一个子视图 .列名也就是子表的字段 ...

  5. 彻底理解javascript中的this指针

    http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/ https://www.benn ...

  6. rsync 排除指定目录

    背景 将Server1上的数据同步到Server2: Server1目录结构: /us_data/yahoo └── qlib ├── calendars ├── dataset_cache ├── ...

  7. FineReport连接ApacheKylin

    1.前言 Apache Kylin™是一个开源的分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由eBay Inc. 开发并贡献至开源社区.它能在 ...

  8. 【转载】 C#中ArrayList使用GetRange方法获取某一段集合数据

    在C#的编程开发中,ArrayList集合是一个常用的非泛型类集合,可以使用GetRange方法来获取集合中指定索引位置开始的一整段集合数据组成一个新的集合,GetRange方法的签名为virtual ...

  9. Java开发环境配置大全

    Java开发环境配置 零章:JDK安装教程 壹章:Tomcat安装教程 贰章:IntelliJ IDEA安装教程 叁章:MySql安装教程 肆章:Maven安装教程 伍章:MongoDB安装教程 陆章 ...

  10. Django 初识之安装下载以及模型目录简介

    Django 一.web应用 web应用什么 Web应用程序是一种可以通过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件 应用程序有两种模式 ...