【Homework】LCA&RMQ
我校是神校,作业竟然选自POJ,难道不知道“珍爱生命 勿刷POJ”么?
所有注明模板题的我都十分傲娇地没有打,于是只打了6道题(其实模板题以前应该打过一部分但懒得找)(不过感觉我模板还是不够溜要找个时间刷一发)。
没注明模板题的都是傻逼题,其实也是模板题。
题目大致按照傻逼程度从大到小排序。
POJ 3264 Balanced Lineup
题意:n个数,m个询问,每次问max[l,r]-min[l,r]。
题解:这道题竟然没标注模板题真是惊讶...
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5e4+; int g[maxn][],h[maxn][];
int n,m; void get(){
for(int j=;(<<j)<=n;j++)
for(int i=;i+(<<j)-<=n;i++)
g[i][j]=min(g[i][j-],g[i+(<<(j-))][j-]),
h[i][j]=max(h[i][j-],h[i+(<<(j-))][j-]);
} int ask(int l,int r){
int k=;
while(<<(k+)<=r-l+) k++;
int Min=min(g[l][k],g[r-(<<k)+][k]);
int Max=max(h[l][k],h[r-(<<k)+][k]);
return Max-Min;
} int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d",&h[i][]);
g[i][]=h[i][];
}
get(); int l,r;
for(int i=;i<=m;i++){
scanf("%d%d",&l,&r);
printf("%d\n",ask(l,r));
}
return ;
}
POJ 3368 Frequent values
题意:n个数组成一个不降序列,m个询问,每次问[l,r]出现次数最多的数。
题解:预处理把相同的数变成一块,查询时二分lr所在块,这部分求RMQ,lr不在块内的单独处理。
这种划分块的细节及边界一定要想清了,手一抖就会错。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1e5+; int a[maxn],b[maxn],c[maxn],t[maxn],tot;
int h[maxn][];
int n,m; void clear(){
memset(h,,sizeof(h));
memset(a,,sizeof(a));
memset(b,,sizeof(b));
memset(c,,sizeof(c));
memset(t,,sizeof(t));
tot=;
} void get(){
for(int i=;i<=tot;i++) h[i][]=t[i];
for(int j=;(<<j)<=tot;j++)
for(int i=;i+(<<j)-<=tot;i++)
h[i][j]=max(h[i][j-],h[i+(<<(j-))][j-]);
} int ask(int l,int r){
if(l>r) return ;
int k=;
while(<<(k+)<=r-l+) k++;
return max(h[l][k],h[r-(<<k)+][k]);
} int main(){ while(scanf("%d%d",&n,&m)!=EOF){
if(n==) break;
clear(); for(int i=;i<=n;i++){
scanf("%d",&a[i]);
if(i==||a[i]!=a[i-]){
++tot;
b[tot]=i;
t[tot]=;
c[tot-]=i-;
}
else t[tot]++;
}
c[tot]=n;
b[tot+]=n+;
get(); int ll,rr,l,r;
for(int i=;i<=m;i++){
scanf("%d%d",&ll,&rr);
l=lower_bound(b+,b+tot+,ll)-b;
r=upper_bound(c+,c+tot+,rr)-c-;
if(r==l-){
printf("%d\n",rr-ll+);
continue;
}
int ans=max(b[l]-ll,rr-c[r]);
ans=max(ans,ask(l,r));
printf("%d\n",ans);
}
}
}
POJ 2763 Housewife Wind
题意:n个节点的树,两个操作,一询问(u,v)距离,二修改某条边边权。
题解:树上的路径长度,很快反应dist[u]+dist[v]-2*dist[lca],这个就像前缀和表示序列一样。
修改一条边对单点到跟链的影响很好统计,利用dfs序+树状数组即可。
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1e5+,POW=; int p[maxn][POW],pw[maxn],que[maxn],idx[maxn],l[maxn],r[maxn],dep[maxn];
int e[maxn*],w[maxn*],nxt[maxn*],head[maxn],tot;
ll d[maxn],c[maxn];
int n,m,s,clock; void dfs(int u,ll dist){
++clock;
que[clock]=u;
l[u]=clock;
d[u]=dist;
dep[u]=dep[p[u][]]+; for(int i=;i<POW;i++) p[u][i]=p[p[u][i-]][i-]; for(int i=head[u];i;i=nxt[i]){
int v=e[i];
if(v==p[u][]) continue;
p[v][]=u,pw[v]=w[i];
dfs(v,dist+w[i]);
}
r[u]=clock;
} int LCA(int u,int v){
if(dep[u]>dep[v]) swap(u,v);
if(dep[u]<dep[v]){
int del=dep[v]-dep[u];
for(int i=;i<POW&&del;i++)
if(del&(<<i)) v=p[v][i];
}
if(u==v) return u;
for(int i=POW-;i>=;i--)
if(p[u][i]!=p[v][i]) u=p[u][i],v=p[v][i];
u=p[u][];
return u;
} void add(int x,int w){
for(int i=x;i>;i-=(i&-i)) c[i]+=w;
}
ll sum(int x){
ll ret=;
for(int i=x;i<=n;i+=(i&-i)) ret+=c[i];
return ret;
} void adde(int u,int v,int g){
++tot;
e[tot]=v,w[tot]=g;
nxt[tot]=head[u];
head[u]=tot;
} int main(){
scanf("%d%d%d",&n,&m,&s);
int u,v,g;
for(int i=;i<n;i++){
scanf("%d%d%d",&u,&v,&g);
adde(u,v,g);
adde(v,u,g);
} dfs(,);
for(int i=;i<=n;i++) idx[que[i]]=i; int x,num;
for(int i=;i<=m;i++){
scanf("%d",&x);
if(x==){
scanf("%d",&u);
int lca=LCA(s,u);
ll ans=d[u]+d[s]-*d[lca];
ans+=(sum(idx[u])+sum(idx[s])-*sum(idx[lca]));
printf("%lld\n",ans);
s=u;
}
else{
scanf("%d%d",&num,&g);
u=e[num*-],v=e[num*];
if(p[v][]!=u) swap(u,v);
add(l[v]-,pw[v]-g);
add(r[v],-pw[v]+g);
pw[v]=g;
}
}
return ;
}
POJ 3728 The merchant
题意:n个节点带权的树,m个询问,问从x->y的路径上max-min为多少,且路径上max位置必须在min位置之前。
题解:分治的思想,我们怎么合并左右两段路?①maxmin全在路1;②全在路2;③max在路1,min在路二。
保存四个量up[x],down[x],min[x],max[x]。
分别代表x->p最优解,p->x最优解,x-p路径上最小值,x-p路径上最大值。
当然还是要用到LCA,如果用trajan就在并查集合并时处理,倍增就更好处理了。
tarjan要想清,要注意答案在lca处统计,下次再码一发倍增。
听说其他同学写了180+行,十分好奇写了些什么>.<
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5e4+; int up[maxn],down[maxn],Min[maxn],Max[maxn];
int n,m; int heade[maxn],e[maxn*],nxte[maxn*];
void adde(int u,int v,int k){
e[k]=v,nxte[k]=heade[u];
heade[u]=k;
} int headq[maxn],q[maxn*],nxtq[maxn*];
void addq(int u,int v,int k){
q[k]=v,nxtq[k]=headq[u];
headq[u]=k;
} int heada[maxn],a[maxn*],nxta[maxn*],tot;
void adda(int u,int num){
++tot;
a[tot]=num,nxta[tot]=heada[u];
heada[u]=tot;
} int p[maxn],vis[maxn],ans[maxn];
void find(int x){
if(p[x]==x) return;
find(p[x]);
up[x]=max(up[x],max(up[p[x]],Max[p[x]]-Min[x]));
down[x]=max(down[x],max(down[p[x]],Max[x]-Min[p[x]]));
Min[x]=min(Min[x],Min[p[x]]);
Max[x]=max(Max[x],Max[p[x]]);
p[x]=p[p[x]];
} void LCA(int u){
vis[u]=;
for(int i=headq[u];i;i=nxtq[i])
if(vis[q[i]]){
int v=q[i];
find(v);
int lca=p[v];
adda(lca,i);
} for(int i=heade[u];i;i=nxte[i])
if(!vis[e[i]]){
int v=e[i];
LCA(v);
p[v]=u;
} for(int i=heada[u];i;i=nxta[i]){
int k=a[i],x,y;
if(k%==) x=q[k+],y=q[k];
else x=q[k],y=q[k-];
find(x),find(y);
k=(k+)/;
ans[k]=max(up[x],max(down[y],Max[y]-Min[x]));
}
} int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&Min[i]);
Max[i]=Min[i];
p[i]=i;
} int u,v;
for(int i=;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v,i*-);
adde(v,u,i*);
} scanf("%d",&m);
for(int i=;i<=m;i++){
scanf("%d%d",&u,&v);
addq(u,v,i*-);
addq(v,u,i*);
} LCA();
for(int i=;i<=m;i++)
printf("%d\n",ans[i]); return ;
}
POJ 3417 Network
题意:n个节点的树,加入m条新边,现在删除一条旧边一条新边使得新的到的图不再联通,求方案数。
题解:乍一看没什么思路,画个图就很好想了。
加入(u,v)这条边,会形成一个u-v-lca的环。
考虑每一条旧边,如果所在环个数为0,那么另一条边怎么选都可以,贡献为m;如果为1,那么必须删除使它形成环的新边,贡献为1;如果>1,弃疗,贡献为0;
然后是常用技巧,打标记然后树形dp一遍。
f[u]++; f[v]++; f[lca]-=2;
对于统计边是这样,如果是统计点就是f[lca]--, f[p[lca]]--, 如Bzoj 松鼠的新家。
#include<cstdio>
#include<vector>
using namespace std;
const int maxn=1e5+; int vis[maxn],f[maxn],p[maxn];
int e[maxn*],nxte[maxn*],heade[maxn],tote;
int q[maxn*],nxtq[maxn*],headq[maxn],totq;
int n,m; int find(int x){return p[x]==x?x:p[x]=find(p[x]);} void adde(int u,int v){
tote++;
e[tote]=v;
nxte[tote]=heade[u];
heade[u]=tote;
} void addq(int u,int v){
totq++;
q[totq]=v;
nxtq[totq]=headq[u];
headq[u]=totq;
} void tarjan(int u){
vis[u]=;
for(int i=headq[u];i;i=nxtq[i])
if(vis[q[i]]){
int v=q[i],lca=find(v);
f[u]++,f[v]++;
f[lca]-=;
} for(int i=heade[u];i;i=nxte[i])
if(!vis[e[i]]){
int v=e[i];
tarjan(v);
p[v]=u;
}
} void dfs(int fa,int u){
for(int i=heade[u];i;i=nxte[i]){
int v=e[i];
if(v==fa) continue;
dfs(u,v);
f[u]+=f[v];
}
} int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) p[i]=i; int u,v;
for(int i=;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v),adde(v,u);
} for(int i=;i<=m;i++){
scanf("%d%d",&u,&v);
addq(u,v),addq(v,u);
} tarjan();
dfs(,); long long ans=;
for(int i=;i<=n;i++)
if(f[i]==) ans++;
else if(!f[i]) ans+=m; printf("%lld\n",ans);
return ;
}
POJ 2374 Fence Obstacle Course
题意:不好描述自己看。
题解:不知道这道题和LCA&RMQ有基本关系。
网上题解都是dp+线段树。
不过我觉得不用线段树,单调队列就可以(怎么单调很好想)。
然而WA了,不理解,拿标称对拍,拍了一大堆数据都没问题,不知道为何WA
欢迎大神帮我查查错。
//6.4 再想了一下单调队列在某些情况下的确有问题,所以还是要线段树,不过为什么对拍都能过,我不知道。。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=5e4+; int a[maxn],b[maxn],anxt[maxn],bnxt[maxn];
int aque[maxn],bque[maxn],at,bt;
ll f[maxn][];
int n,s; int main(){
scanf("%d%d",&n,&s);
for(int i=;i<=n;i++)
scanf("%d%d",&a[i],&b[i]); for(int i=;i<=n;i++){
while(at&&a[aque[at]]>=a[i]) at--;
anxt[i]=aque[at];
aque[++at]=i; while(bt&&b[bque[bt]]<=b[i]) bt--;
bnxt[i]=bque[bt];
bque[++bt]=i;
} memset(f,,sizeof(f));
f[n][]=abs(a[n]-s),f[n][]=abs(b[n]-s); for(int i=n;i>=;i--){
f[anxt[i]][]=min(f[anxt[i]][],f[i][]+abs(a[i]-a[anxt[i]]));
f[anxt[i]][]=min(f[anxt[i]][],f[i][]+abs(a[i]-b[anxt[i]]));
f[bnxt[i]][]=min(f[bnxt[i]][],f[i][]+abs(b[i]-a[bnxt[i]]));
f[bnxt[i]][]=min(f[bnxt[i]][],f[i][]+abs(b[i]-b[bnxt[i]]));
} printf("%lld\n",min(f[][],f[][]));
return ;
}
WA Code
那么我们来总结一下。
感觉LCA常做的有,处理树上路径长度做减法,判断路径是否包含;特殊一点,就是要你统计路径信息,这个就要在求的过程中合并时下功夫;常用的技巧是,DFS+树状数组辅助统计,打标记+树形dp统计。
RMQ貌似还没看到什么比较特殊的。。有一道论文题还可以。。二维RMQ?找个时间填掉。。
【Homework】LCA&RMQ的更多相关文章
- 【BZOJ3626】LCA(树链剖分,Link-Cut Tree)
[BZOJ3626]LCA(树链剖分,Link-Cut Tree) 题面 Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. ...
- 【题解】P4137 Rmq Problem(莫队)
[题解]P4137 Rmq Problem(莫队) 其实这道题根本就不用离散化! 因为显然有\(mex\)值是\(\le 2\times 10^5\)的,所以对于大于\(2\times 10^5\)的 ...
- 【BZOJ-3626】LCA 树链剖分
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1428 Solved: 526[Submit][Status ...
- 【CF52C】Circular RMQ(线段树区间加减,区间最值)
给定一个循环数组a0, a1, a2, …, an-1,现在对他们有两个操作: Inc(le, ri, v):表示区间[le, ri]范围的数值增加v Rmq(le, ri):表示询问区间[le, r ...
- 【模板】LCA
代码如下 #include <bits/stdc++.h> using namespace std; const int maxn=5e5+10; inline int read(){ i ...
- 【LNOI2014】LCA
题面 题解 考察\(dep[\mathrm{LCA}(i, x)]\)的性质,发现它是\(i\)和\(x\)的链交的长度. 那么对每个\(i\)所在的链打一个区间加标记,询问时算一下\(x\)所在的链 ...
- 【BZOJ3626】LCA(树上差分,树链剖分)
题意:给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给 ...
- 【模板】Lca倍增法
Codevs 1036 商务旅行 #include<cstdio> #include<cmath> #include<algorithm> using namesp ...
- bzoj3626【LNOI2014】LCA
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1266 Solved: 448 [Submit][Stat ...
随机推荐
- convert sorted list to binary search tree(将有序链表转成平衡二叉搜索树)
Given a singly linked list where elements are sorted in ascending order, convert it to a height bala ...
- 4 sum
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...
- 开启flume的远程调试功能
各种组件,比如tomcat.storm.flume,我们都可以通过JMX方式开启远程调试,主要可以用来跟踪源码,了解程序内部的运行机制,其次,也有利于你修改源码. 首先,本质上是要修改flume本身启 ...
- Liunx权限修改命令
语法: chmod 数值 文件名 例: chmod 644 mm.txt 命令执行后,文件mm.txt的权限值为 rw-r--r-- 详解: 权限分为 读.写.执行三种,分别用字母 ...
- 【python进阶】深入理解系统进程1
前言 之前程序执⾏都是⼀条腿⾛路,甚⾄是⽤⼀杆枪来打天下. 通过系统编程的学习,会让⼤家有“多条腿”⼀起⾛路,就好⽐有了⼀把机关枪. 此篇为深入理解进程第一篇,下面开始今天的说明~~~ 进程 多任务的 ...
- javaXML文件解析之DOM4J实操
既然前面说了DOM4J这里好那里好,大家都是在用这个,那咱就不得不写一个了. XML文件: <?xml version="1.0" encoding="UTF-8& ...
- c++11线程池
#pragma once #include <future> #include <vector> #include <atomic> #include <qu ...
- [Java算法分析与设计]--顺序栈的实现
在程序的世界,栈的应用是相当广泛的.其后进先出的特性,我们可以应用到诸如计算.遍历.代码格式校对等各个方面.但是你知道栈的底层是怎么实现的吗?现在跟随本篇文章我们来一睹它的庐山真面目吧. 首先我们先定 ...
- c# 事件和EventManager
事件 基本用法 关键字event,声明格式为: public event <委托类型> <事件对象> 事件的处理方法:适用于该委托的方法 数据的触发: 绑定同类事件,绑定时,可 ...
- 凸包问题——Graham Scan
Graham Scan 概述: 对于凸多边形的定义不在这里做详细叙述,这里给出算法的实现原理. Step 1: 找出x值最小的点的集合,从其中找出y值最小的点作为初始点 Step 2: 获得新序列后, ...