红绿 [线段树分治, 并查集]

绿红绿



color{red}{正解部分}

[1,q_tot][1, q\_tot] 进行 线段树分治, 将每个 绿绿 的 影响区间分为 logq_totlog q\_tot 个区间散布在 线段树节点 中,

其中 qtotq_tot 为询问的总数 .

然后对 线段树 DFSDFS, 合并祖先链的和当前节点的绿绿集合 (使用类似归并排序的方法),

由于代价只与 最大生气值 有关, 所以按 生气值 从小到大 排序, 从后往前加入 并查集, 直到有 敌对红红 不得不在一个集合里,

这时保存当前节点贡献, 将数组截止到不合法的位置往下递归 (此时没有遍历到的 绿绿 已无法对答案造成贡献) .

到根节点处统计答案, 时间复杂度 O(N2logN)O(N^2 log N)

并查集维护方法详见 这道题 .


color{red}{实现部分}

  • 注意判断有效区间是否合法 .
  • 并查集合并前需要判断是否有必要 .
  • 为什么代码注释处会 RERE 啊 .
#include<bits/stdc++.h>
#define reg register
#define pb push_back

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 2000005;

int N;
int M;
int Q_;
int q_cnt;
int F[maxn];
int Ans[maxn];
int match[maxn];

char Smp[5];

struct Grn{ int a, b, l, r, v; } A[maxn];

bool cmp(Grn x, Grn y){ return x.v < y.v; }

std::vector <Grn> B[maxn << 2], P[maxn << 2], Tmp;

struct Node{ int l, r; } T[maxn << 2];

int Find(int x){ return F[x]==x?x:F[x]=Find(F[x]); }

void Build(int k, int l, int r){
        T[k].l = l, T[k].r = r;
        if(l == r) return ; int mid = l+r >> 1;
        Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
}

void merge(int a, int b){
        int t1 = Find(a), t2 = Find(b);
        if(t1 == t2) return ;
        F[t1] = t2;
}

void Insert(int k, const Grn &aim){
        int l = T[k].l, r = T[k].r;
        if(aim.l <= l && r <= aim.r) return B[k].pb(aim), void(); 
        int mid = l+r >> 1;
        if(aim.l <= mid) Insert(k<<1, aim);
        if(aim.r > mid) Insert(k<<1|1, aim);
}

void DFS(int k){
        int l = T[k].l, r = T[k].r, res = 0;
        int t1 = 0, t2 = 0, len_1 = P[k>>1].size(), len_2 = B[k].size();
        Tmp.clear();
        while(t1 < len_1 && t2 < len_2){
                if(P[k>>1][t1].v < B[k][t2].v) Tmp.pb(P[k>>1][t1 ++]);
                else Tmp.pb(B[k][t2 ++]);
        }
        while(t2 < len_2) Tmp.pb(B[k][t2 ++]);
        while(t1 < len_1) Tmp.pb(P[k>>1][t1 ++]);
        for(reg int i = 1; i <= 2*N+1; i ++) F[i] = i, match[i] = 0;

        for(reg int i = Tmp.size()-1; i >= 0; i --){
                int u = Tmp[i].a, v = Tmp[i].b;
                if(Find(2*u) == Find(v*2-1)) continue ;
                merge(u*2, v*2-1), merge(v*2, u*2-1);
                P[k].pb(Tmp[i]);
                if(Find(u*2) == Find(u*2-1) || Find(v*2) == Find(v*2-1)){
                        res = Tmp[i].v;
                        break ;
                }
        }

/*
        for(reg int i = Tmp.size()-1; i >= 0; i --){
                Grn t = Tmp[i];
                int a = t.a, b = t.b;
                P[k].pb(Tmp[i]);
                if(Find(a) == Find(b)){ res = t.v; break ; }
                if(!match[a]) match[a] = b; 
                else merge(match[a], b);
                if(!match[b]) match[b] = a;
                else merge(match[b], a);
        }
*/

        if(l == r) return Ans[l] = res, void();
        std::reverse(P[k].begin(), P[k].end());
        DFS(k<<1), DFS(k<<1|1);
}

int main(){
        N = read(), M = read(), Q_ = read();
        for(reg int i = 1; i <= M; i ++){
                A[i].a = read(), A[i].b = read(), A[i].v = read();
                A[i].l = 1, A[i].r = -1;
        }
        for(reg int i = 1; i <= Q_; i ++){
                scanf("%s", Smp);
                if(Smp[0] == 'D')  A[read()].r = q_cnt;
                else if(Smp[0] == 'A'){
                        A[++ M].a = read(), A[M].b = read(), A[M].v = read();
                        A[M].l = q_cnt+1, A[M].r = -1;
                }else q_cnt ++;
        }
        Build(1, 1, q_cnt); std::sort(A+1, A+M+1, cmp);
        for(reg int i = 1; i <= M; i ++){ 
                if(A[i].r == -1) A[i].r = q_cnt;
                if(A[i].l > A[i].r) continue ;
                Insert(1, A[i]); 
        }
        DFS(1);
        for(reg int i = 1; i <= q_cnt; i ++) printf("%d
", Ans[i]);
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822400.html