• 「IOI2018」狼人


    传送门

    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!

  • 相关阅读:
    逆向挑战赛Bob Doge 和 who is he
    [ACTF新生赛2020]rome
    [ACTF新生赛2020]usualCrypt
    MAZE
    $FFT/NTT/FWT$题单&简要题解
    普通型生成函数总结
    [BZOJ4817][SDOI2017]树点涂色:Link-Cut Tree+线段树
    [BZOJ3527][ZJOI2014]力:FFT
    长链剖分优化树形DP总结
    Dsu on Tree总结
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/ioi2018_werewolf.html
Copyright © 2020-2023  润新知