[IOI2018]狼人——kruskal重构树+可持久化线段树
题目链接:
题目大意:给出一张$n$个点$m$条边的无向图,点和边可重复经过,一个狼人初始为人形,有$q$次询问,每次询问要求人形态只能处于编号不小于$L$的点,狼形态只能处于编号不大于$R$的点,询问能否从$S$处于人形态然后在编号在$[L,R]$内的点变身一次成为狼人然后到达 $E$。
题目中编号都是从0开始,太不舒服了,我们按编号从1开始讲QAQ。
题目大意就是询问每次从一个点开始走只能走编号在[l,n]中的点,在任意点变成狼,之后只能走[0,r]中的点,是否能到达另一个点。
后一部分其实就是找有哪些点只走[0,r]中的点能到达终点,那么反过来看,就是终点只走[0,r]中的点能到达哪些点。
那么只要起点能到达的点和终点能到达的点中有交集就有解。
因为起点只能走一些编号较大的点,那么我们求出原图的最大生成树,建出kruskal重构树,求出重构树的dfs序,每次询问在重构树上倍增找能到达的联通块在dfs序上的区间就好了。
相反,从终点走就求出原图的最小生成树,然后也按上述方法找。
找到两段dfs序区间后只要这两段区间中有相同点就能判有解。
我们将每个点在第一个dfs序中的位置作为横坐标,在第二个dfs序中的位置作为纵坐标,剩下的就是一个简单的二维数点问题了。
等会,上面是不是差点什么?原图没有边权啊?
我们以最大生成树为例,对于一条边(x,y),两个点能否通过这条边互相到达,由这两个点中较小的点决定,因此求最大生成树时每条边边权就是边两端点中较小的那个。最小生成树边权就是两端点中较大的那个。
整体思路就是分别建出原图最小生成树和最大生成树的重构树,分别求出每个点在两个重构树中的dfs序位置,然后可持久化线段树二维数点。
kruskal重构树在这里就不赘述了,如果不是太了解可以参考我的另一篇博客NOI2018归程,那道题和这道题思想很像。
题面似乎没说保证整张图联通,因此可能是kruskal重构森林。
#include"werewolf.h"
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
struct miku
{
int u,v,x,y;
}a[400010];
int cnt;
int n,m,k;
int s,t,l,r;
int num1,num2;
int sum1,sum2;
int s1[400010];
int s2[400010];
int t1[400010];
int t2[400010];
int q1[400010];
int q2[400010];
int v1[400010];
int v2[400010];
int fa1[400010];
int fa2[400010];
int ls1[400010];
int rs1[400010];
int ls2[400010];
int rs2[400010];
int ls[5000010];
int rs[5000010];
int vis1[400010];
int vis2[400010];
int sum[5000010];
int root[200010];
int f1[400010][18];
int f2[400010][18];
vector<int>res;
int find1(int x)
{
if(fa1[x]==x)
{
return x;
}
return fa1[x]=find1(fa1[x]);
}
int find2(int x)
{
if(fa2[x]==x)
{
return x;
}
return fa2[x]=find2(fa2[x]);
}
bool cmp1(miku a,miku b)
{
return a.x>b.x;
}
bool cmp2(miku a,miku b)
{
return a.y<b.y;
}
void build(int &rt,int l,int r)
{
rt=++cnt;
if(l==r)
{
return ;
}
int mid=(l+r)>>1;
build(ls[rt],l,mid);
build(rs[rt],mid+1,r);
}
void updata(int &rt,int pre,int l,int r,int k)
{
rt=++cnt;
sum[rt]=sum[pre]+1;
if(l==r)
{
return;
}
ls[rt]=ls[pre];
rs[rt]=rs[pre];
int mid=(l+r)>>1;
if(k<=mid)
{
updata(ls[rt],ls[pre],l,mid,k);
}
else
{
updata(rs[rt],rs[pre],mid+1,r,k);
}
}
int query(int x,int y,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
return sum[y]-sum[x];
}
int mid=(l+r)>>1;
int res=0;
if(L<=mid)
{
res+=query(ls[x],ls[y],l,mid,L,R);
}
if(R>mid)
{
res+=query(rs[x],rs[y],mid+1,r,L,R);
}
return res;
}
void dfs1(int x)
{
vis1[x]=1;
s1[x]=sum1;
if(x<=n)
{
q1[++sum1]=x;
}
for(int i=1;i<=17;i++)
{
f1[x][i]=f1[f1[x][i-1]][i-1];
}
if(ls1[x])
{
dfs1(ls1[x]);
}
if(rs1[x])
{
dfs1(rs1[x]);
}
t1[x]=sum1;
}
void dfs2(int x)
{
vis2[x]=1;
s2[x]=sum2;
if(x<=n)
{
q2[++sum2]=x;
}
for(int i=1;i<=17;i++)
{
f2[x][i]=f2[f2[x][i-1]][i-1];
}
if(ls2[x])
{
dfs2(ls2[x]);
}
if(rs2[x])
{
dfs2(rs2[x]);
}
t2[x]=sum2;
}
int ST1(int x,int val)
{
for(int i=17;i>=0;i--)
{
if(v1[f1[x][i]]>=val&&f1[x][i]!=0)
{
x=f1[x][i];
}
}
return x;
}
int ST2(int x,int val)
{
for(int i=17;i>=0;i--)
{
if(v2[f2[x][i]]<=val&&f2[x][i]!=0)
{
x=f2[x][i];
}
}
return x;
}
vector<int> check_validity(int N,vector<int> X,vector<int> Y,vector<int> S,vector<int> E,vector<int> L,vector<int> R)
{
n=N;
m=X.size();
k=S.size();
for(int i=1;i<=m;i++)
{
a[i].u=X[i-1];
a[i].v=Y[i-1];
a[i].u++;
a[i].v++;
a[i].x=min(a[i].u,a[i].v);
a[i].y=max(a[i].u,a[i].v);
}
for(int i=1;i<2*n;i++)
{
fa1[i]=i;
fa2[i]=i;
}
num1=num2=n;
sort(a+1,a+1+m,cmp1);
for(int i=1;i<=m;i++)
{
int fx=find1(a[i].u);
int fy=find1(a[i].v);
if(fx!=fy)
{
num1++;
v1[num1]=a[i].x;
ls1[num1]=fx;
rs1[num1]=fy;
f1[fx][0]=num1;
f1[fy][0]=num1;
fa1[fx]=num1;
fa1[fy]=num1;
if(num1==2*n-1)
{
break;
}
}
}
for(int i=1;i<=n;i++)
{
if(!vis1[i])
{
dfs1(find1(i));
}
}
sort(a+1,a+1+m,cmp2);
for(int i=1;i<=m;i++)
{
int fx=find2(a[i].u);
int fy=find2(a[i].v);
if(fx!=fy)
{
num2++;
v2[num2]=a[i].y;
ls2[num2]=fx;
rs2[num2]=fy;
f2[fx][0]=num2;
f2[fy][0]=num2;
fa2[fx]=num2;
fa2[fy]=num2;
if(num2==2*n-1)
{
break;
}
}
}
for(int i=1;i<=n;i++)
{
if(!vis2[i])
{
dfs2(find2(i));
}
}
build(root[0],1,n);
for(int i=1;i<=n;i++)
{
updata(root[i],root[i-1],1,n,s2[q1[i]]+1);
}
for(int i=0;i<k;i++)
{
s=S[i],t=E[i],l=L[i],r=R[i];
s++,t++,l++,r++;
s=ST1(s,l);
t=ST2(t,r);
int ans=query(root[s1[s]],root[t1[s]],1,n,s2[t]+1,t2[t]);
ans==0?res.push_back(0):res.push_back(1);
}
return res;
}
[IOI2018]狼人——kruskal重构树+可持久化线段树的更多相关文章
- LOJ.2865.[IOI2018]狼人(Kruskal重构树 主席树)
LOJ 洛谷 这题不就是Peaks(加强版)或者归程么..这算是\(IOI2018\)撞上\(NOI2018\)的题了? \(Kruskal\)重构树(具体是所有点按从小到大/从大到小的顺序,依次加入 ...
- [IOI2018] werewolf 狼人 kruskal重构树,主席树
[IOI2018] werewolf 狼人 LG传送门 kruskal重构树好题. 日常安利博客文章 这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据 ...
- [IOI2018] werewolf 狼人 [kruskal重构树+主席树]
题意: 当你是人形的时候你只能走 \([L,N-1]\) 的编号的点(即大于等于L的点) 当你是狼形的时候你只能走 \([1,R]\) 的编号的点(即小于等于R的点) 然后问题转化成人形和狼形能到的点 ...
- BZOJ3551: [ONTAK2010]Peaks加强版【Kruskal重构树】【主席树】
重要的事情说三遍 不保证图联通 不保证图联通 不保证图联通 那些和我一样认为重构树是点数的童鞋是要GG Description [题目描述]同3545 Input 第一行三个数N,M,Q. 第二行N个 ...
- BZOJ3551 ONTAK2010Peaks加强版(kruskal重构树+dfs序+主席树)
kruskal重构树本质就是给并查集显式建树来替代可持久化并查集.将边按困难度从小到大排序后建出该树,按dfs序建主席树即可.查询时跳到深度最浅的满足在该重要度下已被合并的点,在子树内查询第k大. # ...
- BZOJ 3551: [ONTAK2010]Peaks加强版 [Kruskal重构树 dfs序 主席树]
3551: [ONTAK2010]Peaks加强版 题意:带权图,多组询问与一个点通过边权\(\le lim\)的边连通的点中点权k大值,强制在线 PoPoQQQ大爷题解传送门 说一下感受: 容易发现 ...
- 【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树
这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,L ...
- BZOJ 3551: [ONTAK2010]Peaks加强版 Kruskal重构树+dfs序+主席树+倍增
建出来 $Kruskal$ 重构树. 将询问点向上跳到深度最小,且合法的节点上. 那么,得益于重构树优美的性质,这个最终跳到的点为根的所有子节点都可以与询问点互达. 对于子树中求点权第 $k$ 大的问 ...
- UOJ#407. 【IOI2018】狼人 Kruskal,kruskal重构树,主席树
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ407.html 题解 套路啊. 先按照两个节点顺序各搞一个kruskal重构树,然后问题转化成两棵krus ...
随机推荐
- 【Codeforces 696D】Legen...
Codeforces 696 D 题意:给\(n\)个串,每个串有一个权值\(a_i\),现在要构造一个长度为\(l\leq 10^{14}\)的串,如果其中包含了第\(i\)个串,则会得到\(a_i ...
- 源码篇:Python 实战案例----银行系统
import time import random import pickle import os class Card(object): def __init__(self, cardId, car ...
- java算法----排序----(6)希尔排序(最小增量排序)
package log; public class Test4 { /** * java算法---希尔排序(最小增量排序) * * @param args */ public static void ...
- OI骗分神器——模拟退火算法
前言&&为什么要学模拟退火 最近一下子学了一大堆省选算法,所以搞一个愉快一点的东西来让娱乐一下 其实是为了骗到更多的分,然后证明自己的RP. 说实话模拟退火是一个集物理与IT多方面知识 ...
- 基于Asp.Net Core Mvc和EntityFramework Core 的实战入门教程系列-5
来个目录吧: 第一章-入门 第二章- Entity Framework Core Nuget包管理 第三章-创建.修改.删除.查询 第四章-排序.过滤.分页.分组 第五章-迁移,EF Core 的co ...
- 宇宙最强IDE,查看设计器报错,看不了设计界面
在使用自定义控件或者用户控件的时候,查看设计器打不开界面的原因: Loaded事件中加以下判断条件:if (!DesignerProperties.GetIsInDesignMode(this))
- Iptables防火墙规则使用梳理
iptables是组成Linux平台下的包过滤防火墙,与大多数的Linux软件一样,这个包过滤防火墙是免费的,它可以代替昂贵的商业防火墙解决方案,完成封包过滤.封包重定向和网络地址转换(NAT)等功能 ...
- 五子棋游戏SRS
一.功能需求 1.绘制棋子 2.绘制界面 3.绘制棋盘 4.实现通过鼠标下棋并判断棋子是否落在棋盘上 6.判断胜负 二.用例图 玩家用例图: 1.落子:玩家鼠标点击最近的落子点落子.2.电脑先落子:选 ...
- github链接地址及
http://www.github.com/houyanan1/test.git git 在本地创建分支,并且已经在该分支中开发了一段时间,那么commit到本地后,代码会做一个提交快照,在本地分支保 ...
- 微软开源的Trill是什么?
微软开源的Trill是什么? https://www.cnblogs.com/CattaC/p/10143445.html 微软开源的Trill是什么? 以下是一篇15年的文章的译文:https:// ...