题面

思路

首先,可以确定的是,本题因为每个点只有一条入边,所以整个图肯定是一个基环外向树森林

那么我们首先考虑树上的情况:

我们考虑一个真点,它会对它的子树里面的所有假点产生贡献

一个真点对一个假点的贡献,等于这个真点到这个假点路径上的最大边权(在这个时间之后,他们俩就联通了)

我们要求的是所有贡献的最小值

接下来我们考虑一个假点,可以发现,对它产生贡献的真点,一定是离他最近的祖先

那么我们可以把真点排序,然后使用线段树+区间覆盖的方法来维护每个dfs序区间被哪个真点覆盖了

对于查询最大值,我们可以维护倍增数组解决(题解说用主席树,但是太麻烦了)

接下来我们处理环:

可以发现环上的真点,对于所有这个环外挂的树上的假点都可能产生贡献

我们把环倍长,断成链,离根近的一半不挂外向树,离根远的一半挂载外向树

这样,外向树上的点可以到达任何一个环上的点,环上的点也可以

注意这里需要在一开始线段树覆盖的时候,把环上点的两个地方都加进去

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define end DEEP_DARK_FANTASY
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,m,realn,Q,fa[400010],val[400010],cir[400010],vis[400010],rt[400010],cntr;
int first[400010],cnte;
struct edge{
int to,next;
}a[800010];
inline void add(int u,int v){
a[++cnte]=(edge){v,first[u]};first[u]=cnte;
}
void build(int u){//破环成链
if(!fa[u]){rt[++cntr]=u;vis[u]=2;return;}
if(vis[fa[u]]==1){
int x=u,c;
for(c=fa[u];c!=u;c=fa[c]){
n++;cir[c]=n;
val[n]=val[c];
add(n,x);
fa[x]=n;x=n;
}
cir[u]=++n;add(n,x);
fa[x]=n;rt[++cntr]=n;vis[u]=2;
return;
}
vis[u]=1;
if(!vis[fa[u]]) build(fa[u]);
add(fa[u],u);
vis[u]=2;
return;
}
int dfn[400010],end[400010],maxn[400010][20],st[400010][20],dep[400010],clk;
void dfs(int u,int f){
int i,v;
st[u][0]=f;
dfn[u]=++clk;
maxn[u][0]=val[u];
dep[u]=dep[f]+1;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;
dfs(v,u);
}
end[u]=clk;
}
void ST(){
int i,j;
for(j=1;j<=19;j++)
for(i=1;i<=n;i++)
st[i][j]=st[st[i][j-1]][j-1];
for(j=1;j<=19;j++)
for(i=1;i<=n;i++){
if(dep[i]>(1<<j)) maxn[i][j]=max(maxn[i][j-1],maxn[st[i][j-1]][j-1]);
}
}
int qlist[1000010],cntq,seg[1600010],lazy[1600010],limt;
void push(int l,int r,int num){//区间覆盖线段树
if(l==r||!lazy[num]) return;
seg[num<<1]=seg[num<<1|1]=lazy[num<<1]=lazy[num<<1|1]=lazy[num];
lazy[num]=0;
}
void change(int l,int r,int ql,int qr,int num){
if(l>=ql&&r<=qr){seg[num]=lazy[num]=cntq;return;}
push(l,r,num);
int mid=(l+r)>>1;
if(mid>=ql) change(l,mid,ql,qr,num<<1);
if(mid<qr) change(mid+1,r,ql,qr,num<<1|1);
seg[num]=max(seg[num<<1],seg[num<<1|1]);
}
int query(int l,int r,int pos,int num){
if(l==r) return seg[num];
push(l,r,num);
int mid=(l+r)>>1;
if(mid>=pos) return query(l,mid,pos,num<<1);
else return query(mid+1,r,pos,num<<1|1);
}
int ask(int u){
int pre=query(1,n,dfn[u],1),re=0;
if(pre<=limt) return 1e9;
pre=qlist[pre];
for(int i=19;i>=0;i--){
if(dep[st[u][i]]>=dep[pre]){
re=max(re,maxn[u][i]);
u=st[u][i];
}
}
return re;
}
int tlist[2000010],cntt;
inline bool cmp(int l,int r){
return dep[l]<dep[r];
}
int main(){
memset(first,-1,sizeof(first));
n=read();m=read();int i,t1,t2;
for(i=1;i<=m;i++){
t1=read();t2=read();fa[t2]=t1;
val[t2]=i;
}
realn=n;
for(i=1;i<=realn;i++) if(!vis[i]) build(i);
for(i=1;i<=cntr;i++) dfs(rt[i],0);
ST();
Q=read();
while(Q--){
t1=read();cntt=0;
for(i=1;i<=t1;i++){
t2=read();tlist[++cntt]=t2;
if(cir[t2]) tlist[++cntt]=cir[t2];//插入环上的点的第二个副本
}
sort(tlist+1,tlist+cntt+1,cmp);//按照深度排序
limt=cntq;
for(i=1;i<=cntt;i++){
qlist[++cntq]=tlist[i];
change(1,n,dfn[tlist[i]],end[tlist[i]],1);//覆盖线段树(这里我选择不清空之前的标记,而是直接覆盖,取最大编号)
}
t1=read();int ans=1e9;
for(i=1;i<=t1;i++){
t2=read();
ans=min(ans,ask(t2));
}
if(ans<1e9) printf("%d\n",ans);
else puts("OK");
}
}

Statement [倍增+线段树]的更多相关文章

  1. 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

    题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...

  2. LOJ 北校门外的回忆 倍增+线段树

    正解:倍增+线段树 解题报告: 传送门! $umm$这题有个对正解毫无启发的部分分还有个正解,都挺神仙的所以我都写了趴$QAQ$ 先说部分分 可以考虑把$x$向$x+lowbit(x)$连边,然后当$ ...

  3. 【XSY2534】【BZOJ4817】树点涂色 LCT 倍增 线段树 dfs序

    题目大意 ​ Bob有一棵\(n\)个点的有根树,其中\(1\)号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜 ...

  4. LOJ #2359. 「NOIP2016」天天爱跑步(倍增+线段树合并)

    题意 LOJ #2359. 「NOIP2016」天天爱跑步 题解 考虑把一个玩家的路径 \((x, y)\) 拆成两条,一条是 \(x\) 到 \(lca\) ( \(x, y\) 最近公共祖先) 的 ...

  5. 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并

    [CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...

  6. [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)

    后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...

  7. bzoj3306: 树(dfs序+倍增+线段树)

    比较傻逼的一道题... 显然求子树最小值就是求出dfs序用线段树维护嘛 换根的时候树的形态不会改变,所以我们可以根据相对于根的位置分类讨论. 如果询问的x是根就直接输出整棵树的最小值. 如果询问的x是 ...

  8. BZOJ4556 Tjoi2016&Heoi2016 字符串【后缀自动机+倍增+线段树合并】

    Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开 ...

  9. 倍增/线段树维护树的直径 hdu5993/2016icpc青岛L

    题意: 给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径 点10W,询问10W,询问相互独立 Solution: 考虑线段树/倍增维护树的直径 考虑一个点集的区间 [l, r] 而我们知道了有 ...

随机推荐

  1. git(osChina上分支的使用)

    使用osChina分支的创建分为两种 1.直接在osChina上创建 需要pull否则查看git的状态是不包含改分支的; git pull <git地址/git简称> <分支名> ...

  2. html5的canvas绘制线条,moveTo和lineTo详解

    今天在看html5,里面新增的属性有一个canvas,它相当于一个画布你可以用js在里面画你想要的效果!我在w3c的手册里面看到用moveTo和lineTo绘制线条讲的不是很清楚,尤其是moveTo和 ...

  3. PHPExcel 中文使用手册详解

    /** * * execl数据导出 * 应用场景:订单导出 * @param string $title 模型名(如Member),用于导出生成文件名的前缀 * @param array $cellN ...

  4. python -- configparse读取配置文件

    在开发过程中,有的时候需要将一些参数写入到配置文件中,这样在改动一些相关信息时,可以直接在配置文件中进行修改. 而在python中,可以通过内置模块configparse对标准的配置文件进行读取. 配 ...

  5. flask-login原理详解

    最近发现项目中使用的flask-login中有些bug,直接使用官网的方式确实可以用,但仅仅是可以用,对于原理或解决问题没有什么帮助,最近通过查看网上资料.分析源码.通过demo.从零开始总结了fla ...

  6. RSA前端加密解密

    技术交流群: 233513714 <html> <head> <title>JavaScript RSA Encryption</title> < ...

  7. JWT应用

    调试器库简介问一件T恤! 精心制作 JSON Web令牌简介 新:免费获得JWT手册并深入学习JWT! 什么是JSON Web Token? JSON Web Token(JWT)是一个开放标准(RF ...

  8. nohup 重定向的问题-- 费元星 站长

    费元星 大牛 以前只知道使用nohup可以让一个程序后台执行,但是生成的日志文件都放到nohup.out中了,不能自己指定,尤其是在同一个目录下我需要让两个甚至多个程序都要后台执行时,这样看日志就比较 ...

  9. 剑指Offer - 九度1510 - 替换空格

    剑指Offer - 九度1510 - 替换空格2013-11-29 20:53 题目描述: 请实现一个函数,将一个字符串中的空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之 ...

  10. 《数据结构》C++代码 邻接表与邻接矩阵

    上一篇“BFS与DFS”写完,突然意识到这个可能偏离了“数据结构”的主题,所以回来介绍一下图的存储:邻接表和邻接矩阵. 存图有两种方式,邻接矩阵严格说就是一个bool型的二维数组,map[i][j]表 ...