「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事件,输出一行,表示旅行者记下的数字。
输入输出样例
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)的更多相关文章
- 「SDOI2014」重建 解题报告
「SDOI2014」重建 题意 给一个图\(G\),两点\((u,v)\)有边的概率是\(p_{u,v}\),求有\(n-1\)条边通行且组成了一颗树的概率是多少. 抄了几个矩阵树定理有趣的感性说法 ...
- 「SDOI2014」Lis 解题报告
「SDOI2014」Lis 题目描述 给定序列 \(A\),序列中的每一项 \(A_i\) 有删除代价 \(B_i\) 和附加属性 \(C_i\). 请删除若干项,使得 \(A\) 的最长上升子序列长 ...
- 「SDOI2014」数数 解题报告
「SDOI2014」数数 题目描述 我们称一个正整数 \(N\) 是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(S\) 中任意一个元素作为其子串. 例如当 \(S=(\)22, 333, 0 ...
- 「SDOI2014」向量集 解题报告
「SDOI2014」向量集 维护一个向量集合,在线支持以下操作: A x y :加入向量 \((x, y)\): Q x y l r:询问第 \(L\) 个到第 \(R\) 个加入的向量与向量 \(( ...
- $ybt\ 【信息学奥赛一本通】题解目录$
[信息学奥赛一本通]题解目录 $ \large -> OJ$ $ problem1000 $ \(Answer\) - > $ \large 1000$ $ problem1001 $ \ ...
- 「JSOI2013」旅行时的困惑
「JSOI2013」旅行时的困惑 传送门 由于我们的图不仅是一个 \(\text{DAG}\) 而且在形态上还是一棵树,也就是说我们为了实现节点之间互相可达,就必须把每条边都覆盖一次,因为两个点之间的 ...
- 「JSOI2010」旅行
「JSOI2010」旅行 传送门 比较妙的一道 \(\text{DP}\) 题,思维瓶颈应该就是如何确定状态. 首先将边按边权排序. 如果我们用 \(01\) 串来表示 \(m\) 条边是否在路径上, ...
- 2019寒假练题计划——LibreOJ刷题计划 &《信息学奥赛一本通》提高版题目
目录 2019.1.27 #10082. 「一本通 3.3 例 1」Word Rings 题意 思路 #10083. 「一本通 3.3 例 2」双调路径 题意 思路 #10084. 「一本通 3.3 ...
- 【09NOIP提高组】Hankson 的趣味题(信息学奥赛一本通 1856)(洛谷 1072)
题目描述 Hanks 博士是BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫Hankson.现在,刚刚放学回家的Hankson 正在思考一个有趣的问题.今天在课堂上,老师讲解了如何求 ...
随机推荐
- 创建和使用CI / CD管道【译】【原】
在GitLab 8.8中引入. 介绍 管道是持续集成,交付和部署的顶级组件. 管道包括: 定义要运行的作业的作业.例如,代码编译或测试运行. 定义何时以及如何运行的阶段.例如,该测试仅在代码编译后运行 ...
- 简单聊聊服务发现(redis, zk,etcd, consul)
什么是服务发现? 服务发现并没有怎样的高深莫测,它的原理再简单不过.只是市面上太多文章将服务发现的难度妖魔化,读者被绕的云里雾里,顿觉自己智商低下不敢高攀. 服务提供者是什么,简单点说就是一个HTTP ...
- CodeForces 309B Context Advertising
洛谷题目页面传送门 & CodeForces题目页面传送门 给定一个\(n\)个单词的文本,第\(i\)个单词的长度为\(len_i\),要求截取文本的一段(单词必须取整的),分若干行放,同行 ...
- Linux环境下:vmware安装Windows报错误-缺少所需的CD/DVD驱动器设备驱动程序
解决方法:将硬盘格式从SCSI改为IDE. 方法如下: 右键点击你新建的虚拟机名,点击最下面的setting,看到左侧第二行是hard disk 了么,你那里肯定是SCSI的,选中它,点最下面的rem ...
- python基础--数据结构之字典
字典 特点:无序,键唯一 目录 1.字典的创建 2. .setdefault 的使用 3. 字典中的查找 4.字典中的改 5. 字典中的删除 6. 打印字典的方法 7. 格式化字符串 8. 合并字符 ...
- Solr新特性【4.x,5.x,6.x,7.x】
一.Solr4.x新特性 1.近实时搜索 Solr的近实时搜索[Near Real-Time,NRT]功能实现了文档添加到搜索的快速进行,以应对搜索快速变化的数据. 2.原子更新与乐观并发 原子更新功 ...
- Sql 语句中 IN 和 EXISTS 的区别
IN 语句:只执行一次 确定给定的值是否与子查询或列表中的值相匹配.in在查询的时候,首先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选.所以相对内表比较小的时候,in的速度较快 ...
- prometheus学习系列一: Prometheus简介
Prometheus简介 prometheus受启发于Google的Brogmon监控系统(相似kubernetes是从Brog系统演变而来), 从2012年开始由google工程师Soundclou ...
- 《linux就该这么学》课堂笔记12 网卡配置、防火墙配置
1.网卡配置(四种方法,选其一即可,配置后须重启网络服务使其生效) 1)修改配置文件./etc/sysconfig/network-scripts/ifcfg-网卡名称 2)nmtui [RHEL7] ...
- PAT 乙级 1019.数字黑洞 C++/Java
题目来源 给定任一个各位数字不完全相同的 4 位正整数,如果我们先把 4 个数字按非递增排序,再按非递减排序,然后用第 1 个数字减第 2 个数字,将得到一个新的数字.一直重复这样做,我们很快会停在有 ...