[IOI2018]狼人
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[IOI2018]狼人相关的知识,希望对你有一定的参考价值。
狼人
题解
每个询问要求从我们前半段只能走大于等于
L
L
L的边,后半段只能走小于等于
R
R
R的边。
也就是说,我们前半段经过的节点都是从
S
S
S出发,能到的大于等于
L
L
L的节点,而后半段,则是从
T
T
T出发,能到达的小于等于
R
R
R的节点。
这很明显是一个
K
r
u
s
k
a
l
Kruskal
Kruskal重构树的形式,我们将大于等于
L
L
L的边全部加进去,此时
L
L
L所在的联通块就是它能到达的点。
我们像重构树一样,每次加入边时就建新点连接两个端点所在子树的根,建成一棵新的树。
由于重构树上从叶子到根的链上,节点的点权是单调不升的,我们每次查询就只需要通过倍增找到最浅的点权大于
L
L
L的点即可,这个点的子树内的点,从
S
S
S出发都可以到达。
从
T
T
T出发可到达的节点也可以通过
K
r
u
s
k
a
l
Kruskal
Kruskal重构树方法找到。
由于我们需要一个节点变身,所以我们肯定需要一个两个点出发都能走到的地方。
也就是对于两棵子树,求他们的交集。
显然,子树求交,这是一个经典的区间数点问题。
在子树内的点的
d
f
s
dfs
dfs序是连续的,我们可以将两棵树的
d
f
s
dfs
dfs序列找出来,原问题就变成了查询这两个
d
f
s
dfs
dfs序列的区间中有没有共同点。
我们可以用可持久化线段树来维护区间的点,每次将当前点在另一个序列中的
d
f
s
dfs
dfs序加入线段树,那么我们每次查询只需要差分,看着一段区间内有没有另一个区间中的点即可。
时间复杂度 O ( ( n + m + q ) log n ) O\\left((n+m+q)\\log\\,n\\right) O((n+m+q)logn)
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 400005
#define lowbit(x) (x&-x)
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=400;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-10;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){putchar('\\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,q,fa[MAXN],head[MAXN],tot,idx,idd,root[MAXN];
int dfn1[MAXN],rd1[MAXN],dfn2[MAXN],rd2[MAXN];
int val2[MAXN],f2[MAXN][20],val1[MAXN],f1[MAXN][20];
struct ming{int u,v;}s[MAXN];
struct edge{int to,nxt;}e[MAXN];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
bool cmp1(ming x,ming y){return max(x.u,x.v)<max(y.u,y.v);}
bool cmp2(ming x,ming y){return min(x.u,x.v)>min(y.u,y.v);}
struct tann{
int lson,rson,sum;
tann(){lson=rson=sum=0;}
};
class SegmentTree{
private:
tann tr[MAXN*22];int tot;
public:
void insert(int &now,int las,int l,int r,int ai){
if(l>r||l>ai||r<ai)return ;
tr[now=++tot]=tr[las];tr[now].sum++;
if(l==r)return ;int mid=l+r>>1;
if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai);
if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai);
}
int query(int rt,int l,int r,int al,int ar){
if(l>r||l>ar||r<al||al>ar||!rt)return 0;
if(al<=l&&r<=ar)return tr[rt].sum;int mid=l+r>>1,res=0;
if(al<=mid)res+=query(tr[rt].lson,l,mid,al,ar);
if(ar>mid)res+=query(tr[rt].rson,mid+1,r,al,ar);
return res;
}
}T;
void dosaka1(int u,int fa){
dfn1[u]=idd+1;if(u<=n)idd++;f1[u][0]=fa;
for(int i=1;i<19;i++)f1[u][i]=f1[f1[u][i-1]][i-1];
for(int i=head[u];i;i=e[i].nxt)
dosaka1(e[i].to,u);
rd1[u]=idd;
}
void dosaka2(int u,int fa){
dfn2[u]=idd+1;f2[u][0]=fa;
if(u<=n){idd++;T.insert(root[idd],root[idd-1],1,n,dfn1[u]);}
for(int i=1;i<19;i++)f2[u][i]=f2[f2[u][i-1]][i-1];
for(int i=head[u];i;i=e[i].nxt)
dosaka2(e[i].to,u);
rd2[u]=idd;
}
signed main(){
read(n);read(m);read(q);makeSet(n);idx=n;
for(int i=1;i<=m;i++)read(s[i].u),read(s[i].v),s[i].u++,s[i].v++;
sort(s+1,s+m+1,cmp1);val1[0]=n+1;
for(int i=1;i<=n;i++)val1[i]=val2[i]=i;
for(int i=1;i<=m;i++){
int u=s[i].u,v=s[i].v,w=max(u,v);
if((u=findSet(u))==(v=findSet(v)))continue;
idx++;fa[idx]=fa[u]=fa[v]=idx;val1[idx]=w;
addEdge(idx,u);addEdge(idx,v);
}
for(int i=idx;i>0;i--)if(!dfn1[i])dosaka1(i,0);
for(int i=1;i<=idx;i++)head[i]=0;tot=0;makeSet(n);idx=n;
sort(s+1,s+m+1,cmp2);idd=0;
for(int i=1;i<=m;i++){
int u=s[i].u,v=s[i].v,w=min(u,v);
if((u=findSet(u))==(v=findSet(v)))continue;
idx++;fa[idx]=fa[u]=fa[v]=idx;val2[idx]=w;
addEdge(idx,u);addEdge(idx,v);
}
for(int i=idx;i>0;i--)if(!dfn2[i])dosaka2(i,0);
for(int i=1;i<=q;i++){
int s,t,l,r;read(s);read(t);read(l);read(r);s++;t++;l2018 女生赛 F - 赛题分析