luogu P4899 [IOI2018] werewolf 狼火
首先很显然,从人形起点出发能到的点和狼形能到终点的点都是一个联通块,如果能从起点到终点则说明这两个联通块有交
这个时候可以请出我们的克鲁斯卡尔重构树,即对原图分别建两棵重构树,一棵边权为两端点较小值,边权从大到小排序后构建,另一棵反过来,所以构完后第一棵重构树某个点权值要小于等于子树内的点,第二棵某点权值大于等于子树内点
对于一个询问,起点在第一棵树向上倍增找深度最浅,权值\(\ge l\)的祖先x,终点在第二棵树向上倍增找深度最浅,权值\(\le l\)的祖先y,那么x和y就是上面说到的联通块.现在问题转化为子树判交,把询问离线,把y挂在x上,在第一棵树内从叶子开始向上做,做到某个点就x查询对应y子树对应的dfn区间在线段树上有没有x子树的叶子节点
代码极丑qwq
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define db double
#define il inline
#define re register
using namespace std;
const int N=2e5+10;
const db eps=1e-5;
il LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int ed[N<<2];
struct edge
{
int x,y,w;
}ee[N<<1];
int ff[N<<1];
il int findf(int x){return ff[x]==x?x:ff[x]=findf(ff[x]);}
struct trr
{
int a[N<<1],fa[N<<1][20],ch[N<<1][2],sz[N<<1],dfn[N<<1],tt,s[N<<1],tp;
trr(){tp=0;}
il void init(int n){tt=n;}
il void ad(int x,int y,int w){a[++tt]=w,ff[x]=ff[y]=fa[x][0]=fa[y][0]=tt,ch[tt][0]=x,ch[tt][1]=y;}
void pree(int x)
{
if(!x) return;
s[++tp]=x,dfn[x]=tp,sz[x]=1;
for(int j=1;j<20;++j)
{
fa[x][j]=fa[fa[x][j-1]][j-1];
if(!fa[x][j]) break;
}
pree(ch[x][0]),pree(ch[x][1]);
sz[x]+=sz[ch[x][0]]+sz[ch[x][1]];
}
il int findd(int x,int p,bool op)
{
if(op) {for(int j=19;~j;--j) if(a[fa[x][j]]>=p) x=fa[x][j];}
else {for(int j=19;~j;--j) if(fa[x][j]&&a[fa[x][j]]<=p) x=fa[x][j];}
return x;
}
}tr[2];
il bool cmp1(edge a,edge b){return a.w>b.w;}
il bool cmp2(edge a,edge b){return a.w<b.w;}
int qto[N<<1],qnt[N<<1],qhd[N<<1],qt;
il void qwq(int x,int y){++qt,qto[qt]=y,qnt[qt]=qhd[x],qhd[x]=qt;}
int n,m,q;
bool an[N];
struct sgmt
{
bool s[(N<<1)*30];
int ch[(N<<1)*30][2],rt[N<<1],tt;
sgmt(){tt=0;}
il void inst(int o,int x)
{
int l=1,r=n+n-1;
s[o]=1;
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid) o=ch[o][0]=++tt,r=mid;
else o=ch[o][1]=++tt,l=mid+1;
s[o]=1;
}
}
int merge(int o1,int o2)
{
if(!o1||!o2) return o1+o2;
if(o1==o2) return o1;
int o=++tt;
s[o]=s[o1]|s[o2];
ch[o][0]=merge(ch[o1][0],ch[o2][0]);
ch[o][1]=merge(ch[o1][1],ch[o2][1]);
return o;
}
bool quer(int o,int l,int r,int ll,int rr)
{
if(!o) return 0;
if(ll<=l&&r<=rr) return s[o];
bool an=0;
int mid=(l+r)>>1;
if(ll<=mid) an|=quer(ch[o][0],l,mid,ll,rr);
if(rr>mid) an|=quer(ch[o][1],mid+1,r,ll,rr);
return an;
}
}sht;
int main()
{
n=rd(),m=rd(),q=rd();
tr[0].init(n),tr[1].init(n);
for(int i=1;i<=n;++i) tr[0].a[i]=tr[1].a[i]=i;
for(int i=1;i<=m;++i) ed[i<<1]=rd()+1,ed[i<<1|1]=rd()+1;
for(int i=1;i<=m;++i) ee[i]=(edge){ed[i<<1],ed[i<<1|1],min(ed[i<<1],ed[i<<1|1])};
sort(ee+1,ee+m+1,cmp1);
for(int i=1;i<n+n;++i) ff[i]=i;
for(int i=1;i<=m;++i) if(findf(ee[i].x)^findf(ee[i].y)) tr[0].ad(ff[ee[i].x],ff[ee[i].y],ee[i].w);
tr[0].pree(n+n-1);
for(int i=1;i<=m;++i) ee[i]=(edge){ed[i<<1],ed[i<<1|1],max(ed[i<<1],ed[i<<1|1])};
sort(ee+1,ee+m+1,cmp2);
for(int i=1;i<n+n;++i) ff[i]=i;
for(int i=1;i<=m;++i) if(findf(ee[i].x)^findf(ee[i].y)) tr[1].ad(ff[ee[i].x],ff[ee[i].y],ee[i].w);
tr[1].pree(n+n-1);
for(int i=1;i<=q;++i)
{
int x=rd()+1,y=rd()+1,l=rd()+1,r=rd()+1;
x=tr[0].findd(x,l,1),y=tr[1].findd(y,r,0);
qwq(x,y);
}
for(int i=n+n-1;i;--i)
{
int x=tr[0].s[i];
if(x<=n) sht.inst(sht.rt[x]=++sht.tt,tr[1].dfn[x]);
else sht.rt[x]=sht.merge(sht.rt[tr[0].ch[x][0]],sht.rt[tr[0].ch[x][1]]);
for(int j=qhd[x];j;j=qnt[j])
{
int xx=qto[j];
an[j]=sht.quer(sht.rt[x],1,n+n-1,tr[1].dfn[xx],tr[1].dfn[xx]+tr[1].sz[xx]-1);
}
}
for(int i=1;i<=q;++i) printf("%d\n",an[i]);
return 0;
}
luogu P4899 [IOI2018] werewolf 狼火的更多相关文章
- [LOJ2865] P4899 [IOI2018] werewolf 狼人
P4899 [IOI2018] werewolf 狼人 LOJ#2865.「IOI2018」狼人,第一次AC交互题 kruskal 重构树+主席树 其实知道重构树的算法的话,难度就主要在主席树上 习惯 ...
- p4899 [IOI2018] werewolf 狼人
分析 我用的主席树维护qwq 代码 #include<iostream> #include<cstdio> #include<cstring> #include&l ...
- [IOI2018] werewolf 狼人
[IOI2018] werewolf 狼人 IOI2018题解 (其实原题强制在线,要用主席树) 代码: 注意: 1.下标从0~n-1 2.kruskal重构树开始有n个节点,tot从n开始,++to ...
- [IOI2018] werewolf 狼人 kruskal重构树,主席树
[IOI2018] werewolf 狼人 LG传送门 kruskal重构树好题. 日常安利博客文章 这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据 ...
- 题解 洛谷 P4899 【[IOI2018] werewolf 狼人】
先考虑狼形,其只能走编号小于\(R\)的点.若将每条边赋边权为其两端点编号的较大值,然后按最小生成树的顺序构建\(Kruskal\)重构树. 那么从原图的一个点\(x\)在树上倍增,到达满足要求且深度 ...
- P4899 【[IOI2018] werewolf 狼人】
感觉已经几次碰到这种类型的题目了,写篇\(Blog\)总结一下 题意: 是否存在一条\((s_i, t_i)\)的路径,满足先只走编号不超过\(L_i\)的点,再走编号不超过\(R_i\)的点 \(S ...
- Luogu4899 IOI2018 Werewolf 主席树、Kruskal重构树
传送门 IOI强行交互可还行,我Luogu的代码要改很多才能交到UOJ去-- 发现问题是对边权做限制的连通块类问题,考虑\(Kruskal\)重构树进行解决. 对于图上的边\((u,v)(u<v ...
- [Luogu4899][IOI2018] werewolf 狼人
luogu sol \(\mbox{IOI2018}\)的出题人有没有看过\(\mbox{NOI2018}\)的题目呀... \(\mbox{Kruskal}\)重构树+二维数点. 题目相当于是问你从 ...
- [IOI2018] werewolf 狼人 [kruskal重构树+主席树]
题意: 当你是人形的时候你只能走 \([L,N-1]\) 的编号的点(即大于等于L的点) 当你是狼形的时候你只能走 \([1,R]\) 的编号的点(即小于等于R的点) 然后问题转化成人形和狼形能到的点 ...
随机推荐
- 【Asia Yokohama Regional Contest 2018】Arithmetic Progressions
题目大意:给定 N(1<N<=5000) 个不同元素组成的集合,求从中选出若干数字组成的等差数列最长是多少. 题解:直接暴力有 \(O(n^3)\) 的算法,即:枚举等差数列的前两个值,再 ...
- configure - 源代码安装的第一步
configure是源代码安装的第一步,主要的作用是对即将安装的软件进行配置,检查当前的环境是否满足要安装软件的依赖关系 configure有许多参数可配,具体参见./configure --help ...
- python基础之logging
import logging def logger(): logger = logging.getLogger('logger')#括号里面默认是root logger.setLevel('DEBUG ...
- python操作excel文件一(xlrd读取文件)
一般做接口测试,会把参数和一些数据放入excel表中,这样就不会重新编译代码,提高效率.一般如何操作呢?接下来跟着步骤一起学习吧 执行步骤: 1.首先要安装 xlrd这个模块,用 pip instal ...
- 为什么fork()2次会避免产生僵尸进程
什么是僵尸进程:用fork()创建子进程后,子进程已终止但父进程没有对它进行善后处理,那么子进程的进程描述符就一直保存在内存中,子进程就是僵尸进程. 怎么产生僵尸进程: 1.父进程没有SIGCHLD信 ...
- 【清北学堂2018-刷题冲刺】Contest 4
Task 1:序列 [问题描述] 小H原本有一个由连续的正整数组成的序列,如{4,5,6}或{10,11,12,13,14,15,16},但是她最近睡眠不足,只能记得其中的一些数字.她想知道,她最少 ...
- OpenCV编译以及QT Creator配置
OpenCV编译以及QT Creator配置 在进行编译前,需下载以下工具和源码: CMake ---- 用于编译: 下载地址; https://cmake.org/ 安装在D:\Program Fi ...
- qml: 自定义滚动条;
注: 该博文为原创博文,转载请注明,摘用请随意: qml自带的滚动条不太好用: 首先,利用canvas绘制滚动条两端的箭头: import QtQuick 2.0 Canvas { width: 2 ...
- C++ MFC------ 快捷键
创建快捷键 该文讲解如何给dialog的菜单或者按钮添加快捷键. 1.局部快捷键 点击控件,右键点“属性”,在属性Caption后添加&Key,例如: &Y,即通过按Alt+Y即可触发 ...
- CodeForces922E DP//多重背包的二进制优化
https://cn.vjudge.net/problem/1365218/origin 题意 一条直线上有n棵树 每棵树上有ci只鸟 在一棵树底下召唤一只鸟的魔法代价是costi 每召唤一只鸟,魔法 ...