1. 程式人生 > >[IOI2018]werewolf狼人——kruskal重構樹+可持久化線段樹

[IOI2018]werewolf狼人——kruskal重構樹+可持久化線段樹

lse style 都是 pda 位置 roo sca com font

題目鏈接:

IOI2018werewolf

題目中編號都是從0開始,太不舒服了,我們按編號從1開始講QAQ。

題目大意就是詢問每次從一個點開始走只能走編號在[l,n]中的點,在任意點變成狼,之後只能走[0,r]中的點,是否能到達另一個點。

後一部分其實就是找有哪些點只走[0,r]中的點能到達終點,那麽反過來看,就是終點只走[0,r]中的點能到達哪些點。

那麽只要起點能到達的點和終點能到達的點中有交集就有解。

因為起點只能走一些編號較大的點,那麽我們求出原圖的最大生成樹,建出kruskal重構樹,求出重構樹的dfs序,每次詢問在重構樹上倍增找能到達的聯通塊在dfs序上的區間就好了。

相反,從終點走就求出原圖的最小生成樹,然後也按上述方法找。

找到兩段dfs序區間後只要這兩段區間中有相同點就能判有解。

我們將每個點在第一個dfs序中的位置作為橫坐標,在第二個dfs序中的位置作為縱坐標,剩下的就是一個簡單的二維數點問題了。

等會,上面是不是差點什麽?原圖沒有邊權啊?

我們以最大生成樹為例,對於一條邊(x,y),兩個點能否通過這條邊互相到達,由這兩個點中較小的點決定,因此求最大生成樹時每條邊邊權就是邊兩端點中較小的那個。最小生成樹邊權就是兩端點中較大的那個。

整體思路就是分別建出原圖最小生成樹和最大生成樹的重構樹,分別求出每個點在兩個重構樹中的dfs序位置,然後可持久化線段樹二維數點。

kruskal重構樹在這裏就不贅述了,如果不是太了解可以參考我的另一篇博客NOI2018歸程

,那道題和這道題思想很像。

題面似乎沒說保證整張圖聯通,因此可能是kruskal重構森林。

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
struct miku
{
    int u,v,x,y;
}a[400010];
int cnt;
int n,m,k;
int s,t,l,r;
int num1,num2;
int sum1,sum2;
int s1[400010];
int s2[400010];
int t1[400010];
int t2[400010];
int q1[400010];
int q2[400010];
int v1[400010];
int v2[400010];
int fa1[400010];
int fa2[400010];
int ls1[400010];
int rs1[400010];
int ls2[400010];
int rs2[400010];
int ls[5000010];
int rs[5000010];
int vis1[400010];
int vis2[400010];
int sum[5000010];
int root[200010];
int f1[400010][18];
int f2[400010][18];
int find1(int x)
{
    if(fa1[x]==x)
    {
        return x;
    }
    return fa1[x]=find1(fa1[x]);
}
int find2(int x)
{
    if(fa2[x]==x)
    {
        return x;
    }
    return fa2[x]=find2(fa2[x]);
}
bool cmp1(miku a,miku b)
{
    return a.x>b.x;
}
bool cmp2(miku a,miku b)
{
    return a.y<b.y;
}
void build(int &rt,int l,int r)
{
    rt=++cnt;
    if(l==r)
    {
        return ;
    }
    int mid=(l+r)>>1;
    build(ls[rt],l,mid);
    build(rs[rt],mid+1,r);
}
void updata(int &rt,int pre,int l,int r,int k)
{
    rt=++cnt;
    sum[rt]=sum[pre]+1;
    if(l==r)
    {
        return;
    }
    ls[rt]=ls[pre];
    rs[rt]=rs[pre];
    int mid=(l+r)>>1;
    if(k<=mid)
    {
        updata(ls[rt],ls[pre],l,mid,k);
    }
    else
    {
        updata(rs[rt],rs[pre],mid+1,r,k);
    }
}
int query(int x,int y,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
    {
        return sum[y]-sum[x];
    }
    int mid=(l+r)>>1;
    int res=0;
    if(L<=mid)
    {
        res+=query(ls[x],ls[y],l,mid,L,R);
    }
    if(R>mid)
    {
        res+=query(rs[x],rs[y],mid+1,r,L,R);
    }
    return res;
}
void dfs1(int x)
{
    vis1[x]=1;
    s1[x]=sum1;
    if(x<=n)
    {
        q1[++sum1]=x;
    }
    for(int i=1;i<=17;i++)
    {
        f1[x][i]=f1[f1[x][i-1]][i-1];
    }
    if(ls1[x])
    {
        dfs1(ls1[x]);
    }
    if(rs1[x])
    {
        dfs1(rs1[x]);
    }
    t1[x]=sum1;
}
void dfs2(int x)
{
    vis2[x]=1;
    s2[x]=sum2;
    if(x<=n)
    {
        q2[++sum2]=x;
    }
    for(int i=1;i<=17;i++)
    {
        f2[x][i]=f2[f2[x][i-1]][i-1];
    }
    if(ls2[x])
    {
        dfs2(ls2[x]);
    }
    if(rs2[x])
    {
        dfs2(rs2[x]);
    }
    t2[x]=sum2;
}
int ST1(int x,int val)
{
    for(int i=17;i>=0;i--)
    {
        if(v1[f1[x][i]]>=val&&f1[x][i]!=0)
        {
            x=f1[x][i];
        }
    }
    return x;
}
int ST2(int x,int val)
{
    for(int i=17;i>=0;i--)
    {
        if(v2[f2[x][i]]<=val&&f2[x][i]!=0)
        {
            x=f2[x][i];
        }
    }
    return x;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a[i].u,&a[i].v);
        a[i].u++;
        a[i].v++;
        a[i].x=min(a[i].u,a[i].v);
        a[i].y=max(a[i].u,a[i].v);
    }
    for(int i=1;i<2*n;i++)
    {
        fa1[i]=i;
        fa2[i]=i;
    }
    num1=num2=n;
    sort(a+1,a+1+m,cmp1);
    for(int i=1;i<=m;i++)
    {
        int fx=find1(a[i].u);
        int fy=find1(a[i].v);
        if(fx!=fy)
        {
            num1++;
            v1[num1]=a[i].x;
            ls1[num1]=fx;
            rs1[num1]=fy;
            f1[fx][0]=num1;
            f1[fy][0]=num1;
            fa1[fx]=num1;
            fa1[fy]=num1;
            if(num1==2*n-1)
            {
                break;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis1[i])
        {
            dfs1(find1(i));
        }
    }
    sort(a+1,a+1+m,cmp2);
    for(int i=1;i<=m;i++)
    {
        int fx=find2(a[i].u);
        int fy=find2(a[i].v);
        if(fx!=fy)
        {
            num2++;
            v2[num2]=a[i].y;
            ls2[num2]=fx;
            rs2[num2]=fy;
            f2[fx][0]=num2;
            f2[fy][0]=num2;
            fa2[fx]=num2;
            fa2[fy]=num2;
            if(num2==2*n-1)
            {
                break;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis2[i])
        {
            dfs2(find2(i));
        }
    }
    build(root[0],1,n);
    for(int i=1;i<=n;i++)
    {
        updata(root[i],root[i-1],1,n,s2[q1[i]]+1);
    }
    while(k--)
    {
        scanf("%d%d%d%d",&s,&t,&l,&r);
        s++;
        t++;
        l++;
        r++;
        s=ST1(s,l);
        t=ST2(t,r);
        int ans=query(root[s1[s]],root[t1[s]],1,n,s2[t]+1,t2[t]);
        if(ans==0)
        {
            printf("0\n");
        }
        else
        {
            printf("1\n");
        }
    }
}

[IOI2018]werewolf狼人——kruskal重構樹+可持久化線段樹