首页 > 其他分享 >CF1801E/CF1802G

CF1801E/CF1802G

时间:2023-03-15 09:33:39浏览次数:32  
标签:f2 CF1801E ith int CF1802G ST 1ll find

思路

设 \(x_i\) 为从 \(a\) 到 \(b\) 的简单路径上的节点, \(y_i\) 从 \(c\) 到 \(d\) 的简单路径上的节点。

那么每次操作就是使 \(x_i\) 的汽油价格和 \(y_i\) 一致,这个东西是具有传递性的,可以用并查集维护。

直接暴力做的复杂度是无法通过本题的。太对不起1024MB的内存了

考虑维护更多的信息。不明显的,我们可以用并查集维护长度为 \(2^i\) 的段。

具体地,设 \(f2[i][x]\) 表示以 \(x\) 为端点的长度为 \(2^i\) 的向上的段所在的集合, \(f2[i][x+n]\) 表示以 \(x\) 为端点的长度为 \(2^i\) 的向下的段所在的集合。

仍然考虑暴力合并两个长度为 \(2^i\) 的段:

  1. 若 \(i=0\) 则直接合并两个点。

  2. 若 \(f2[i][x]\) 和 \(f2[i][y]\) 在同一个集合中,直接退出。

  3. 否则递归分别合并两个长度 \(2^{i-1}\) 的段,并合并 \(f2[i][x]\) 和 \(f2[i][y]\) 所在的集合。

在实际中合并两个逆向的段和合并两个同向的段略有不同,请读者注意。

暴力合并两个点:

// 合并两个点
void merge(int x,int y){
    x=find(x,f1),y=find(y,f1);
    if(x==y)return;
    ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[x]-L[x]+1)),mod-2)%mod;
    ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[y]-L[y]+1)),mod-2)%mod; // 撤销贡献
    f1[x]=y;
    L[y]=max(L[x],L[y]);
    R[y]=min(R[x],R[y]);
    ans=1ll*ans*1ll*1ll*max(0ll,1ll*(R[y]-L[y]+1))%mod; // 加上新贡献
}

暴力合并两个长度为 \(2^i\) 的同向段:

// 合并同向等长段
void merge1(int x,int y,int ith){
    if(find(x,f2[ith])==find(y,f2[ith]))return; // 剪枝
    if(ith==0)return merge(x,y),void(); // 直接合并
    f2[ith][find(x,f2[ith])]=find(y,f2[ith]); // 并查集合并
    merge1(x,y,ith-1);
    merge1(ST[x][ith-1],ST[y][ith-1],ith-1); // 暴力合并两个子段
}

暴力合并两个长度为 \(2^i\) 的逆向段:

// 合并逆向等长段
void merge2(int x,int y,int ith){
    if(find(x,f2[ith])==find(y+n,f2[ith]))return; // 剪枝
    if(ith==0)return merge(x,y),void();
    f2[ith][find(x+n,f2[ith])]=find(y,f2[ith]);// 并查集合并
    f2[ith][find(x,f2[ith])]=find(y+n,f2[ith]);
    merge2(x,ST[y][ith-1],ith-1); // 与合并同向等长段不同
    merge2(ST[x][ith-1],y,ith-1); // 暴力合并两个子段
}

总代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN=2e5+11,mod=1e9+7;

int ST[MAXN][18],depth[MAXN];

int h[MAXN],nt[MAXN<<1],to[MAXN<<1],linkcnt;

int L[MAXN],R[MAXN],n,ans=1;

int f1[MAXN],f2[18][MAXN<<1];

int ksm(int x,int y){
    int cur=1;
    for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)cur=1ll*cur*x%mod;
    return cur;
}

// UNION_FIND_SETS
int find(int u,int *UNION_FIND_SETS){
    return UNION_FIND_SETS[u]==u?u:UNION_FIND_SETS[u]=find(UNION_FIND_SETS[u],UNION_FIND_SETS);
}

void link(int u,int v){
    nt[++linkcnt]=h[u],h[u]=linkcnt,to[linkcnt]=v;
}

void dfs_init(int u,int father){
    depth[u]=depth[father]+1;

    f1[u]=u;
    for(int i=0; i<=17; i++)
        f2[i][u]=u,f2[i][u+n]=u+n;

    ST[u][0]=father;
    for(int i=1; i<=17; i++)
        ST[u][i]=ST[ST[u][i-1]][i-1];

    for(int i=h[u]; i; i=nt[i]){
        if(to[i]==father)continue;
        dfs_init(to[i],u);
    }
}

int LCA(int u,int v){
    if(depth[u]<=depth[v])swap(u,v);
    for(int i=17; i>=0; i--)if(depth[ST[u][i]]>=depth[v])u=ST[u][i];
    if(u==v)return u;
    for(int i=17; i>=0; i--)if(ST[u][i]!=ST[v][i])u=ST[u][i],v=ST[v][i];
    return ST[u][0];
}

int kth_fa(int u,int dist){
    for(int i=0; i<=17; i++)
        if(((1<<i)&dist))u=ST[u][i];
    return u;
}

// 合并两个点
void merge(int x,int y){
    x=find(x,f1),y=find(y,f1);
    if(x==y)return;
    ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[x]-L[x]+1)),mod-2)%mod;
    ans=1ll*ans*1ll*ksm(max(0ll,1ll*(R[y]-L[y]+1)),mod-2)%mod;
    f1[x]=y;
    L[y]=max(L[x],L[y]);
    R[y]=min(R[x],R[y]);
    ans=1ll*ans*1ll*1ll*max(0ll,1ll*(R[y]-L[y]+1))%mod;
}

// 合并同向等长段
void merge1(int x,int y,int ith){
    if(find(x,f2[ith])==find(y,f2[ith]))return;
    if(ith==0)return merge(x,y),void();
    f2[ith][find(x,f2[ith])]=find(y,f2[ith]);
    merge1(x,y,ith-1);
    merge1(ST[x][ith-1],ST[y][ith-1],ith-1);
}

// 合并逆向等长段
void merge2(int x,int y,int ith){
    if(find(x,f2[ith])==find(y+n,f2[ith]))return;
    if(ith==0)return merge(x,y),void();
    f2[ith][find(x+n,f2[ith])]=find(y,f2[ith]);
    f2[ith][find(x,f2[ith])]=find(y+n,f2[ith]);
    merge2(x,ST[y][ith-1],ith-1);
    merge2(ST[x][ith-1],y,ith-1);
}

int main(){
    scanf("%d",&n);
    for(int i=2; i<=n; i++){
        static int f;
        scanf("%d",&f);
        link(f,i);
    }
    dfs_init(1,0);
    for(int i=1; i<=n; i++){
        scanf("%d %d",&L[i],&R[i]);
        ans=1ll*1ll*ans*max(0ll,1ll*(R[i]-L[i]+1))%mod;
    }
    int Q_sum;
    scanf("%d",&Q_sum);
    for(int Text=1; Text<=Q_sum; Text++){
        int a,b,c,d,lca_of_a_b,lca_of_c_d,T;
        scanf("%d %d %d %d",&a,&b,&c,&d);
        
        lca_of_a_b=LCA(a,b);
        lca_of_c_d=LCA(c,d);
        
        // 合并同向等长段
        T=min(depth[a]-depth[lca_of_a_b],depth[c]-depth[lca_of_c_d]);
        for(int i=17; i>=0; i--)if((T&(1<<i)))merge1(a,c,i),a=ST[a][i],c=ST[c][i];

        T=min(depth[b]-depth[lca_of_a_b],depth[d]-depth[lca_of_c_d]);
        for(int i=17; i>=0; i--)if((T&(1<<i)))merge1(b,d,i),b=ST[b][i],d=ST[d][i];

        // 合并逆向等长段
        if(a==lca_of_a_b){
            T=depth[b]-depth[lca_of_a_b]+1;
            for(int i=17; i>=0; i--){
                if((T&(1<<i))){
                    T^=(1<<i);
                    merge2(b,kth_fa(c,T),i);
                    b=ST[b][i];
                }
            }
        }
        else{
            T=depth[a]-depth[lca_of_a_b]+1;
            for(int i=17; i>=0; i--){
                if((T&(1<<i))){
                    T^=(1<<i);
                    merge2(a,kth_fa(d,T),i);
                    a=ST[a][i];
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

标签:f2,CF1801E,ith,int,CF1802G,ST,1ll,find
From: https://www.cnblogs.com/dadidididi/p/17217313.html

相关文章