题目大意:给定n个点m条边的无向简单连通图,k次询问,每次给出两个点u,v和两个限制l,r,询问是否有一条从u到v的简单路径满足:路径上有一个分界点,前一半经过的点(包括u和分界点)的编号都>=l,后一半经过的点(包括v和分界点)的编号都<=r。
全场大约十几人ac,我们全机房一块研究了一天终于搞出了一个像样的做法。(顺便%一下某ckw同学,看到题面16分钟直接秒掉!)
这么工业的题一看就是中国人出的。这道题是一道对中国选手比较友好的题目,全场17人ac,为yml没有现场a掉感到可惜。
我们分别考虑两个限制。以l来说,当l=n时我们哪也走不了,随着l减小,能到达的区域在渐渐合并,最后合并成一个连通块。
是不是有点像kruskal最小生成树的过程?
那么我们就需要考虑维护两棵生成树,然后动态查询两部分分别拎出一个连通块来,它们是否有交集。
去过今年noi的很容易发现,这不就是noi d1t1用到的黑科技——kruskal生成树嘛!
(此处安利noi d1t1题解:https://blog.csdn.net/liuzhangfeiabc/article/details/81114640)
具体来说,每次我们选出下一个节点,把它能合并的那些连通块的根节点的父亲全部指向它,然后把它当做新的根。
(还有一点省事之处就是连新建节点都不需要,因为我们是天然地按照点权顺序合并。)
于是问题就变成了:从两棵树上各选出一个子树(用倍增查询),问两个子树有没有公共点。
这就变成了经典的二维数点问题,可以用各种数据结构维护。这里选用主席树的原因是,这样我们甚至能支持强制在线!
//以下代码根据ioi提交格式编写
#include<bits/stdc++.h>
using namespace std;
#include"werewolf.h"
int n,m,k;
struct edge{
int to,nxt;
}e[1000010];
int cnt,fir[200010];
void ins(int u,int v){
e[++cnt].to = v;e[cnt].nxt = fir[u];fir[u] = cnt;
e[++cnt].to = u;e[cnt].nxt = fir[v];fir[v] = cnt;
}
int f[200010];
int getf(int q){
return f[q] == q ? q : f[q] = getf(f[q]);
}
int fsts1[200010],nxt1[200010],fsts2[200010],nxt2[200010];
int st1[20][200010],st2[20][200010];
int nw1,nw2,wz1[200010],wz2[200010],ed1[200010],ed2[200010],dfsx2[200010];
void dfs1(int q){
wz1[q] = ++nw1;
for(int i = fsts1[q];i;i = nxt1[i]) dfs1(i);
ed1[q] = nw1;
}
void dfs2(int q){
wz2[q] = ++nw2;
dfsx2[nw2] = q;
for(int i = fsts2[q];i;i = nxt2[i]) dfs2(i);
ed2[q] = nw2;
}
inline void buildst(){
register int i,j;
for(i = 1;i < 20;++i) for(j = 1;j <= n;++j) st1[i][j] = st1[i - 1][st1[i - 1][j]],st2[i][j] = st2[i - 1][st2[i - 1][j]];
}
int rt[200010],ct,t[5000010],ls[5000010],rs[5000010];
#define ln ls[q],l,mid
#define rn rs[q],mid + 1,r
#define md int mid = l + r >> 1
void ins(int p,int &q,int l,int r,int x){
q = ++ct;
if(l == r){
t[q] = t[p] + 1;
return;
}
md;
if(mid >= x){
ins(ls[p],ln,x);
rs[q] = rs[p];
}
else{
ls[q] = ls[p];
ins(rs[p],rn,x);
}
t[q] = t[ls[q]] + t[rs[q]];
}
int qy(int q,int l,int r,int al,int ar){
if(!q) return 0;
if(l >= al && r <= ar) return t[q];
md;
int as = 0;
if(mid >= al) as = qy(ln,al,ar);
if(mid < ar) as += qy(rn,al,ar);
return as;
}
inline int find1(int u,int x){
for(int i = 19;i >= 0;--i) if(st1[i][u] >= x) u = st1[i][u];
return u;
}
inline int find2(int u,int x){
for(int i = 19;i >= 0;--i) if(st2[i][u] && st2[i][u] <= x) u = st2[i][u];
return u;
}
vector<int> as;
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();as.resize(k);
int i,j,u,v,l,r;
for(i = 0;i < m;++i){
u = _x[i] + 1;v = _y[i] + 1;ins(u,v);
}
for(i = 1;i <= n;++i) f[i] = i;
for(i = n;i;--i) for(j = fir[i];j;j = e[j].nxt) if(e[j].to > i){
l = getf(e[j].to);if(l == i) continue;
f[l] = st1[0][l] = i;nxt1[l] = fsts1[i];fsts1[i] = l;
}
for(i = 1;i <= n;++i) f[i] = i;
for(i = 1;i <= n;++i) for(j = fir[i];j;j = e[j].nxt) if(e[j].to < i){
l = getf(e[j].to);if(l == i) continue;
f[l] = st2[0][l] = i;nxt2[l] = fsts2[i];fsts2[i] = l;
}
dfs1(1);dfs2(n);
buildst();
for(i = 1;i <= n;++i) ins(rt[i - 1],rt[i],1,n,wz1[dfsx2[i]]);
for(i = 0;i < k;++i){
u = _s[i] + 1;v = _e[i] + 1;l = _l[i] + 1;r = _r[i] + 1;
if(u < l || v > r || l > r){
as[i] = 0;
continue;
}
u = find1(u,l);v = find2(v,r);
as[i] = qy(rt[ed2[v]],1,n,wz1[u],ed1[u]) - qy(rt[wz2[v] - 1],1,n,wz1[u],ed1[u]) > 0;
}
return as;
}