注意看,我耗时五个小时 AK 了 IOI

题意

给你一个图,每次给定若干询问 \((s,t,l,r)\),请你完成下述要求:

  • 定义 \(S\) 为到 \(s\) 的最短路径不小于 \(l\) 的点构成的子图,\(T\) 为到 \(t\) 的最短路径不大于 \(r\) 的点构成的子图
  • 请你判断 \(S\) 与 \(T\) 是否有交集

解法

当询问次数不大时做法显然,每次对 \(s,t\) 跑 dfs 即可

但是此题询问量大,考虑如何预处理

一 寻找子图

路径最小最大,容易想到使用 Kruskal 生成树. 我们尝试对整张图建立 Kruskal 最小和最大生成树,根据其性质在树上跑 LCA 来 \(\log\) 解决这部分问题.

但是在实际实现的时候会遇到一些问题:我们仍然还需要枚举所有点,因此最坏情况下需要单次 \(n\log n\) 求解,复杂度不够优秀.

现在转化问题:注意到我们求解出来的子图一定是树结构,由 dfs 序的性质,最终这颗子树一定是原树 dfs 序上连续的一段,因此我们将问题抽象成这样:

给定两个序列 \(S,T\),询问是否存在 \(i\in[l,n]\),使得存在 \(T_{j}=S_{i},j\in[1,r]\)

其中 \(S\) 是我们建立的 Kruskal 最小生成树的 dfs 序,\(T\) 是建立的 Kruskal 最大生成树的 dfs 序,这样我们就通过两遍 Kruskal+LCA 转化了这个问题

二 求解交集

注意到我们可以建立一个数组 \(f_{i}\) 表示 \(i\) 在 \(S\) 中的位置,这样建立一个映射是为了方便后续操作:我们可以通过调用 \(f_{T_{i}}\) 来查看 \(T_{i}\) 在 \(S\) 中的位置

因此,我们可以进一步转化这个问题:

给定两个序列 \(S,T\),询问是否存在 \(i\in[1,r]\),使得 \(f_{T_{i}}\in[l,n]\)

注意到我们现在把问题转化成了一种类似求值域的东西,因此考虑对 \(f_{T_{i}}\) 开可持久化线段树,维护前缀和,每次把 \(r,l-1\) 两颗可持久化线段树传下去作差维护答案,这个问题即可求解.

下面是这一部分,可持久化线段树内的 ask() 函数代码

bool ask(int p,int q,int l,int r,int L,int R){ //存在则返回 true
if(l>R or L>r) return false;
if(L<=l and r<=R) return t[q].cnt-t[p].cnt; //满足[1,r]条件后还需要作差
int mid(l,r);
return ask(t[q].l,t[p].l,l,mid,L,R) or ask(t[q].r,t[p].r,mid+1,r,L,R);
}

比较简单。

三 整合

那我们刚才弄出来的 Kruskal 生成树显然就是用来找,我们需要的 dfs 序的左右端点(也即哪两颗可持久化线段树需要被传下去)了,这一部分暴力倍增即可。

注意事项

  • 在 loj 版里,需要引用 "werewolf.h"
  • 求端点的倍增别弄反了,应该是先大再小
  • 因为用了 Kruskal 生成树,因此空间需要开到 \(2n\)

代码

#include<bits/stdc++.h>
#include"werewolf.h"
using namespace std;
const int inf=0x7fffffff;
int n,m,q;
struct edge{
int from,to;
};
edge ed[400001];
int a[400001],b[400001];
int cnt=0;
bool cmp1(const edge &A,const edge &B){
return max(A.from,A.to)<max(B.from,B.to);
}
bool cmp2(const edge &A,const edge &B){
return min(A.from,A.to)>min(B.from,B.to);
}
int mp[400001],t[400001],root[400001];
class dsu{
private:
int fa[400001];
public:
void clear(){
for(int i=1;i<=2*n;++i){
fa[i]=i;
}
}
int find(int id){
if(id==fa[id]) return id;
fa[id]=find(fa[id]);
return fa[id];
}
void join(int x,int y){ //add x to y (fa[x]=y)
int fx=find(x),fy=find(y);
if(fx!=fy){
fa[fx]=fy;
}
}
};
class kruskal{
public:
int tot,treecnt;
int fa[20][400001],w[400001],l[400001],r[400001];
vector<int>e[400001];
dsu d;
void clear(){
tot=n;
d.clear();
memset(l,0x3f,sizeof l);
}
void dfs(int now,int type){
if(now<=n){
if(type==1){
a[++treecnt]=now;
}
else{
b[++treecnt]=now;
}
l[now]=r[now]=treecnt;
return;
}
for(int i:e[now]){
if(i==fa[0][now]) continue;
fa[0][i]=now;
dfs(i,type);
l[now]=min(l[now],l[i]);
r[now]=max(r[now],r[i]);
}
}
void build(int type){
clear();
if(type==1){
sort(ed+1,ed+m+1,cmp1);
}
else{
sort(ed+1,ed+m+1,cmp2);
}
for(int i=1;i<=m;++i){
if(d.find(ed[i].from)!=d.find(ed[i].to)){
e[++tot].push_back(d.find(ed[i].from));
e[tot].push_back(d.find(ed[i].to));
e[d.find(ed[i].from)].push_back(tot);
e[d.find(ed[i].to)].push_back(tot);
d.join(ed[i].from,tot);
d.join(ed[i].to,tot);
if(type==1){
w[tot]=max(ed[i].from,ed[i].to);
}
else{
w[tot]=min(ed[i].from,ed[i].to);
}
}
}
if(type==1) w[0]=inf;
else w[0]=-inf;
dfs(tot,type);
for(int j=1;j<=19;++j){
for(int i=1;i<=tot;++i){
fa[j][i]=fa[j-1][fa[j-1][i]];
}
}
}
int get_version(int now,int l,int r){
for(int i=19;~i;--i){
if(w[fa[i][now]]>=l and w[fa[i][now]]<=r){
now=fa[i][now];
}
}
return now;
}
}t1,t2;
class HIST_Stree{
public:
#define mid(l,r) mid=((l)+(r))/2
int tot=0;
struct tree{
int l,r;
int cnt;
}t[400001*25];
int clone(int id){
t[++tot]=t[id];
return tot;
}
int insert(int id,int l,int r,int pos){
int q=clone(id);
if(l==r){
t[q].cnt++;
return q;
}
int mid(l,r);
if(pos<=mid){
t[q].l=insert(t[id].l,l,mid,pos);
}
else{
t[q].r=insert(t[id].r,mid+1,r,pos);
}
t[q].cnt=t[t[q].l].cnt+t[t[q].r].cnt;
return q;
}
bool ask(int p,int q,int l,int r,int L,int R){
if(l>R or L>r) return false;
if(L<=l and r<=R) return t[q].cnt-t[p].cnt;
int mid(l,r);
return ask(t[q].l,t[p].l,l,mid,L,R) or ask(t[q].r,t[p].r,mid+1,r,L,R);
}
}hist;
vector<int> check_validity(int N, vector<int> X, vector<int> Y, vector<int> U, vector<int> V, vector<int> L, vector<int> R) {
n=N,m=X.size(),q=U.size();
vector<int>ans;
for(int i=1;i<=m;++i){
ed[i]={X[i-1]+1,Y[i-1]+1};
}
t1.build(1);
t2.build(2);
for(int i=1;i<=n;++i){
t[a[i]]=i;
}
for(int i=1;i<=n;++i){
mp[i]=t[b[i]];
}
for(int i=1;i<=n;++i){
root[i]=hist.insert(root[i-1],1,n,mp[i]);
}
for(int i=1;i<=q;++i){
int s=U[i-1]+1,t=V[i-1]+1;
int l=L[i-1]+1,r=R[i-1]+1;
swap(s,t);
s=t1.get_version(s,1,r);
t=t2.get_version(t,l,n);
if(hist.ask(root[t2.r[t]],root[t2.l[t]-1],1,n,t1.l[s],t1.r[s])){
ans.push_back(1);
}
else{
ans.push_back(0);
}
}
return ans;
}
// vector<int> X,Y,U,V,L,R;
// int main(){
// cin>>n>>m>>q;
// for(int i=1;i<=m;++i){
// int a,b;
// cin>>a>>b;
// X.push_back(a);Y.push_back(b);
// }
// for(int i=1;i<=q;++i){
// int s,e,l,r;
// cin>>s>>e>>l>>r;
// U.push_back(s);
// V.push_back(e);
// L.push_back(l);
// R.push_back(r);
// }
// vector<int> ans=check_validity(n,X,Y,U,V,L,R);
// for(int i:ans){
// cout<<i<<endl;
// }
// }

[Tkey] [IOI 2018] werewolf的更多相关文章

  1. [IOI 2018] Werewolf

    [题目链接] https://www.luogu.org/problemnew/show/P4899 [算法]         建出原图的最小/最大生成树的kruskal重构树然后二维数点 时间复杂度 ...

  2. 【IOI 2018】Werewolf 狼人

    虽然作为IOI的Day1T3,但其实不是一道很难的题,或者说这道题其实比较套路吧. 接下来讲解一下这个题的做法: 如果你做过NOI 2018的Day1T1,并且看懂了题面,那你很快就会联想到这道题,因 ...

  3. 【IOI 2018】Combo 组合动作(模拟,小技巧)

    题目链接 IOI的签到题感觉比NOI的签到题要简单啊,至少NOI同步赛我没有签到成功…… 其实这个题还是挺妙妙的,如果能够从题目出发,利用好限制,应该是可以想到的做法的. 接下来开始讲解具体的做法: ...

  4. 【IOI 2018】Highway 高速公路收费

    这是一道极好的图论题,虽然我一开始只会做$18$分,后来会做$51$分,看着题解想了好久才会做(吐槽官方题解:永远只有一句话),但这的确是一道好题,值得思考,也能启发思维. 如果要讲这道题,还是要从部 ...

  5. 【IOI 2018】Doll 机械娃娃

    我感觉这个题作为Day2T1,有一定的挑战性.为$Rxd$没有完成这道题可惜. 我觉得这道题,如果按照前几个部分分的思路来想,就有可能绕进错误的思路中.因为比如说每个传感器最多只在序列中出现$2$次, ...

  6. Before NOIP 2018

    目录 总结 刷题 2018 - 9 - 24 2018 - 9 - 25 2018 - 9 - 26 2018 - 9 - 27 2018 - 9 - 28 2018 - 9 - 29 2018 - ...

  7. HDU - 6370 Werewolf 2018 Multi-University Training Contest 6 (DFS找环)

    求确定身份的人的个数. 只能确定狼的身份,因为只能找到谁说了谎.但一个人是否是民,无法确定. 将人视作点,指认关系视作边,有狼边和民边两种边. 确定狼的方法只有两种: 1. 在一个仅由一条狼边组成的环 ...

  8. [日常] PKUWC 2018爆零记

    吃枣药丸...先开个坑... day -1 上午周测...大翻车... 下午被查水表说明天必须啥啥啥...(当时我差点笑出声) 晚上领到笔记本一枚和一袋耗材(袜子) 然而班会开太晚回去没来得及收拾就晚 ...

  9. OI生涯回忆录 2018.11.12~2019.4.15

    上一篇:OI生涯回忆录 2017.9.10~2018.11.11 一次逆风而行的成功,是什么都无法代替的 ………… 历经艰难 我还在走着 一 NOIP之后,全机房开始了省选知识的自学. 动态DP,LC ...

  10. CTSC 2018 游记

    day0 李总提前一天放假,回家颓整理行李... 然而我... 早上:睡觉... 中午:睡觉... 晚上:睡觉去火车站... 吃了几把鸡,本来想带李总入坑,但他挥手拒绝然后被李总带进了炸金花的坑... ...

随机推荐

  1. LM Studio + open-webui 快速本地部署大语言模型

    目录 一.前言 二.环境准备 三.安装设置 四.下载模型并运行 五.配置 open-webui 写在结尾 一.前言 自 OpenAi 发布 ChatGPT 对话性大语言模型,AI 这两年发展迎来爆发, ...

  2. Linux中&&、&、|、||等特殊符号

    && 和 & & 表示任务后台执行,与nohup命令功能差不多. # 运行jar包,并且置于后台执行,执行的日志重定向到当前默认的log.txt文件中 [root@lo ...

  3. 【Java,IDEA】创建自己的代码模版快速生成

    写原生JavaWeb发现一个问题就是声明方法的时候没有字符关联提示, 只能一个保留字,一个保留字这样单个的敲出来方法,写多了就会发现特别费劲 当遇上一个字特别多且经常需要声明的方法可以使用IDEA的生 ...

  4. 使用CPU运行大语言模型(LLM),以清华开源大模型ChatGLM3为例:无需显卡!用CPU搞定大模型运行部署!【详细手把手演示】

    教程视频地址: 无需显卡!用CPU搞定大模型运行部署![详细手把手演示] 按照上面视频进行安装配置之前需要注意,python编程环境需要大于等于python3.10,否则会运行报错.下载好GitHub ...

  5. 国产操作系统 “银河麒麟操作系统V10” 试用失败历程

    面对外国的科技封锁,具有自主产权的国产软件已经变得迫在眉睫了,几天前在新闻上看到国产的操作"银河麒麟操作系统V10"已经发布,于是抱着尝鲜的心态想着去试着用用.虽然都是基于linu ...

  6. PyTorch+昇腾 共促AI生态创新发展

    原文链接: https://mp.weixin.qq.com/s/s8jNzTo0DM_LjyUwYDVgGg ============================================ ...

  7. 多网卡系统下如何使用tcp协议实现MPI的分布式多机运行(mpi的实现使用openmpi)

    如题: 最近在看MPI方面的东西,主要是Python下的MPI4PY,学校有超算机房可以使用MPI,但是需要申请什么的比较麻烦,目的也本就是为了学习一下,所以就想着在自己的电脑上先配置一下. 现有硬件 ...

  8. 使用TensorFlow、Pytorch等深度学习框架时如何设置对OpenCV的使用

    如题: 在使用深度学习框架时如果同时也在使用opencv那么有一些设置是需要设定的,第一个就是在python代码中设定禁止使用opencl: cv2.ocl.setUseOpenCL(False) o ...

  9. 运维 + AI,你得先搞懂这些

    很感谢夜莺提供如此优质的平台能和行业内顶尖技术大佬做面对面的交流,在这个会议中又学习到了很多有趣有深度的内容,给我在未来探索的道路上提供了一些新的指引方向.同时感谢夜莺社区的邀请,在此再做一次关于AI ...

  10. # games101 作业3分析 详解bump mapping

    games101 作业3分析 详解bump mapping 代码分析 整体代码结构 其实变化还是不大 主要是引入了vertexshader(什么都没做) 与 fragmentshader(使用了不同的 ...