【Kruskal重构树+二维数点(离线BIT)】LOJ2865 IOI2018 狼人(werewolf)

【题目】
原题地址
给定一幅无向图,多次询问从 S i S_i Si出发到 T i T_i Ti,是否存在一条路径 S i − > x − > T i S_i->x->T_i Si>x>Ti,满足 S i − > x S_i->x Si>x经过的点编号 ≤ x \leq x x x − > T i x->T_i x>Ti经过的点的编号 ≥ x \geq x x L i ≤ x ≤ R i L_i\leq x \leq R_i LixRi,数字级别 2 × 1 0 5 2\times 10^5 2×105

【解题思路】
这题实际上有点类似NOI2018的D1T1,只不过这题的限制已经在点上了。

仍然考虑重构树,我们需要构建一棵树,使得任意一个非根节点的标号都比它的父亲大。

我们只需要从大到小枚举一个点 x x x,尝试将它加入树中,枚举所有与它相邻且编号比它大的节点 y y y,如果二者不在一个连通块中,就让 y y y的并查集的根成为 x x x的儿子,再在并查集中连起来。

那么现在从 S S S出发只走大于等于 L i L_i Li的节点能到的所有点,在树上对应了一个子树。我们可以用树上倍增找到这个子树。

同理从小到大再做一次,可以得到从 T T T出发只走小于等于 R i R_i Ri的节点能到的所点也对应了一个子树。

d f s dfs dfs序可以转化为区间问题,那么现在的问题就是在这两个区间中是否同时拥有某个元素。

这里每个元素在一个序列中都恰好只出现了一次,我们只需要将一个元素在两个 d f s dfs dfs序中出现的位置 p 1 , p 2 p_1,p_2 p1,p2看作一个二维数点,即 ( p 1 , p 2 ) (p_1,p_2) (p1,p2),那么问题就转化为了判断平面内一个矩形中是否有点,这是一个经典问题,离线 B I T BIT BIT即可。

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

【参考代码(交互格式)】

#include "werewolf.h"
#include
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
#define vi vector 
#define lowbit(x) (x&(-x))
using namespace std;

typedef pair pii;
const int N=4e5+10,M=19;
int n,m,q,cnt,tot;
int head[N],pos[N],fa[N],ans[N],tr[N<<1];
int dfn[2][N],siz[2][N],f1[M][N],f2[M][N],c1[M][N],c2[M][N];
pii rd[N<<1];

struct Tway{int u,v;}e[N];
struct Tline{int id,l,r,c;};
vi Tans;
vectorline[N];

bool cmp1(const Tway&A,const Tway&B){return min(A.u,A.v)>min(B.u,B.v);}
bool cmp2(const Tway&A,const Tway&B){return max(A.u,A.v)=y) x=f1[j][x];
		int l1=dfn[0][x],r1=l1+siz[0][x]-1;
		x=E[i];y=R[i];
		for(int j=17;~j;--j) if(f2[j][x] && c2[j][x]<=y) x=f2[j][x];
		int l2=dfn[1][x],r2=l2+siz[1][x]-1;
		line[l1-1].pb((Tline){i,l2,r2,-1});
		line[r1].pb((Tline){i,l2,r2,1});
		//printf("%d %d %d %d\n",l1,r1,l2,r2);
	}

	for(int i=0;i

【总结】
套路啊套路。

你可能感兴趣的:(其他-杂题,图论-Kruskal重构树)