传送门
Description
在日本的茨城县内共有 (N) 个城市和 (M) 条道路。这些城市是根据人口数量的升序排列的,依次编号为 (0) 到 (N−1)。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。
你计划了 (Q) 个行程,这些行程分别编号为 (0) 至 Q−1。第 i((0le ile Q-1))个行程是从城市 (S_i) 到城市 (E_i)。
你是一个狼人。你有两种形态:人形和狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在 (S_i) 或 (E_i)内)变身。
狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程 (i)((0le ile Q-1)),都有两个阈值 (L_i) 和 (R_i)((0le L_ile R_ile N-1)),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市 (0,1,cdots,L_{i-1});而当你是狼形时,则必须避开城市 (R_i+1,R_i+2,cdots,N-1)。这就是说,在行程 ii 中,你必须在城市 (L_i,L_i+1,cdots,R_i)中的其中一个城市内变身。
你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市 (S_i)走到城市 (E_i)。你的路线可以有任意长度。
Solution
首先,考虑分别建出大根和小根的kruskal重构树,因为本题的边权其实就是点的编号,所以每次合并时可以不需要新建节点,直接把当前权值大小的那个点当作根就可以了
然后,对于每个询问,满足条件等价于有一个可行的中转点,这个中转点应该满足同时处于两个重构树的某两个子树内,用主席树进行二维数点
Code
#include "werewolf.h"
#include<vector>
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MN=8e5+5,MM=4e5+5;
int N,M,Q,S,E,L,R;
struct edge{int to,nex;}e[MM<<1],e1[MN],e2[MN];
int en,en1,en2,hr[MN],hr1[MN],hr2[MN],siz1[MN],siz2[MN],fd1[MN],dfn1[MN],dfn2[MN],dind,fa1[MN][19],fa2[MN][19];
inline void Ins(int f,int t){e[++en]=(edge){t,hr[f]};hr[f]=en;}
inline void ins1(int x,int y){e1[++en1]=(edge){y,hr1[x]};hr1[x]=en1;}
inline void ins2(int x,int y){e2[++en2]=(edge){y,hr2[x]};hr2[x]=en2;}
inline void ins(int f,int t){Ins(f,t);Ins(t,f);}
int rt[MN],ls[MN*16],rs[MN*16],v[MN*16],tt;
int par[MN];inline int getf(int x){return par[x]==x?x:par[x]=getf(par[x]);}
inline void build()
{
register int x,y,i,j;
for(i=1;i<=N;++i)par[i]=i;
for(i=N;i;--i)for(j=hr[i];j;j=e[j].nex)if(e[j].to>i)
{x=getf(i),y=getf(e[j].to);if(x==y)continue;par[y]=x,ins1(x,y);}
for(i=1;i<=N;++i)par[i]=i;
for(i=1;i<=N;++i)for(j=hr[i];j;j=e[j].nex)if(e[j].to<i)
{x=getf(i),y=getf(e[j].to);if(x==y)continue;par[y]=x,ins2(x,y);}
}
void dfs1(int x,int f)
{
fa1[x][0]=f;dfn1[x]=++dind;fd1[dind]=x;siz1[x]=1;register int i;
for(i=hr1[x];i;i=e1[i].nex)dfs1(e1[i].to,x),siz1[x]+=siz1[e1[i].to];
}
void dfs2(int x,int f)
{
fa2[x][0]=f;dfn2[x]=++dind;siz2[x]=1;register int i;
for(i=hr2[x];i;i=e2[i].nex)dfs2(e2[i].to,x),siz2[x]+=siz2[e2[i].to];
}
inline void init()
{
dind=0;dfs1(1,0);register int i,j;
for(j=1;j<=18;++j)for(i=1;i<=N;++i)fa1[i][j]=fa1[fa1[i][j-1]][j-1];
dind=0;dfs2(N,0);
for(j=1;j<=18;++j)for(i=1;i<=N;++i)fa2[i][j]=fa2[fa2[i][j-1]][j-1];
}
inline int Find1(int x,int m){for(int j=18;~j;--j)if(fa1[x][j]>=m&&fa1[x][j]!=0)x=fa1[x][j];return x;}
inline int Find2(int x,int m){for(int j=18;~j;--j)if(fa2[x][j]<=m&&fa2[x][j]!=0)x=fa2[x][j];return x;}
void Md(int &x,int l,int r,int o)
{
++tt;ls[tt]=ls[x];rs[tt]=rs[x];v[tt]=v[x]+1;x=tt;if(l==r)return;
int mid=(l+r)>>1;if(o<=mid)Md(ls[x],l,mid,o);else Md(rs[x],mid+1,r,o);
}
int Qu(int rt1,int rt2,int l,int r,int a,int b)
{
if(a==l&&b==r)return v[rt1]-v[rt2];
int mid=(l+r)>>1;
if(b<=mid) return Qu(ls[rt1],ls[rt2],l,mid,a,b);
else if(a>mid) return Qu(rs[rt1],rs[rt2],mid+1,r,a,b);
return Qu(ls[rt1],ls[rt2],l,mid,a,mid)+Qu(rs[rt1],rs[rt2],mid+1,r,mid+1,b);
}
using std::vector;
vector<int> check_validity(int Nn, vector<int> X, vector<int> Y, vector<int> Ss, vector<int> Ee, vector<int> Ll, vector<int> Rr)
{
M=X.size();Q=Ss.size();N=Nn;
register int i;
for(i=0;i<M;++i) ins(X[i]+1,Y[i]+1);
build();init();
for(i=1;i<=N;++i) rt[i]=rt[i-1],Md(rt[i],1,N,dfn2[fd1[i]]);
vector<int> ans(Q,0);
for(i=0;i<Q;++i)
{
S=Ss[i]+1;E=Ee[i]+1;L=Ll[i]+1;R=Rr[i]+1;
register int _1=Find1(S,L),_2=Find2(E,R);
if(Qu(rt[dfn1[_1]+siz1[_1]-1],rt[dfn1[_1]-1],1,N,dfn2[_2],dfn2[_2]+siz2[_2]-1)>0) ans[i]=1;
}
return ans;
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!