zoukankan      html  css  js  c++  java
  • P5025 [SNOI2017]炸弹 [线段树优化建图 + Tarjan]

    炸弹

    题目描述见上方链接 .


    color{red}{正解部分}

    • 用线段树优化建图建图, 得到一个边数为 NlogNNlogN 的图 .

    不会的可以看这里 线段树优化建图

    • 然后使用 TarjanTarjan 将图 缩点 变成 DAGDAG, 时间复杂度O(NlogN)O(NlogN)

    不会的可以看这里 TarjanTarjan

    • 在联通块之间连边, 注意与前面的边区分开 .

    • 对每个连通块 DFSDFS, 统计答案 .


    这里给出样例建出的图

    在这里插入图片描述带红色的节点为 出树 节点 .


    color{red}{实现部分}

    • 线段树优化建图时记得树内连边 .
    • 不是图中的节点不能计入 联通块的大小, 仅标记在 入树 中的叶子节点即可 .
    • 建完图后, 基本上用的只是 线段树 中的节点了, 所以要注意不要混淆 线段树原图 的节点编号 .
    • 判重边, 否则 DFSDFS 时会答案被加多 .

    82pts82pts 代码, 这里使用了两个线段树, 因此后面的点 TLETLE, 建一颗线段树可以 ACAC .

    #include<set>
    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #define reg register
    #define pb push_back
    typedef long long ll;
    
    const int maxn = 6e6 + 105;
    const int mod = 1e9 + 7;
    
    int N;
    int num_1;
    int num_2;
    int dfn_tim;
    int node_cnt;
    int block_num;
    int rot[2];
    ll X[maxn];
    ll Y[maxn];
    ll R[maxn];
    int head[maxn];
    int head_2[maxn];
    int dfn[maxn];
    int low[maxn];
    int Ans[maxn];
    int block_id[maxn];
    int block_size[maxn];
    int Mp[2][maxn];
    
    bool is_g[maxn];
    bool In_stk[maxn];
    
    std::vector <int> block[maxn];
    std::stack <int> stk;
    std::set <int> sete[maxn];
    
    struct Edge{ int nxt, to; } edge[maxn], edge_2[maxn];
    
    struct Node{ int l, r, lt, rt; } T[maxn<<2];
    
    void Add(int from, int to){
            edge[++ num_1] = (Edge){ head[from], to };
            head[from] = num_1;
    }
    
    void Add_2(int from, int to){
            edge_2[++ num_2] = (Edge){ head_2[from], to };
            head_2[from] = num_2;
    }
    
    void Build(int &k, int l, int r, int opt){ //
            k = ++ node_cnt;
            T[k].l = l, T[k].r = r;
            if(l == r){ is_g[k] = !opt, Mp[opt][l] = k; return ; }
            int mid = l+r >> 1;
            Build(T[k].lt, l, mid, opt), Build(T[k].rt, mid+1, r, opt);
            if(!opt) Add(T[k].lt, k), Add(T[k].rt, k);
            else Add(k, T[k].lt), Add(k, T[k].rt);
    }
    
    void Connect_0(int k){ //
            int l = T[k].l, r = T[k].r;
            if(l == r){ Add(Mp[1][l], Mp[0][l]); return ; }
            Connect_0(T[k].lt), Connect_0(T[k].rt);
    }
    
    void Connect_1(int k, int L, int R, int o_id){ // 1
            int l = T[k].l, r = T[k].r;
            if(L <= l && r <= R){
                    if(l == r && o_id == l) return ;
                    Add(Mp[0][o_id], k); return ;
            }
            int mid = l+r >> 1;
            if(L <= mid) Connect_1(T[k].lt, L, R, o_id);
            if(R > mid)  Connect_1(T[k].rt, L, R, o_id);
    }
    
    void Tarjan(int k){
            In_stk[k] = 1; stk.push(k);
            low[k] = dfn[k] = ++ dfn_tim;
            for(reg int i = head[k]; i; i = edge[i].nxt){
                    int to = edge[i].to;
                    if(!dfn[to]) Tarjan(to), low[k] = std::min(low[k], low[to]);
                    else if(In_stk[to]) low[k] = std::min(low[k], dfn[to]); //#
            }
            if(dfn[k] == low[k]){
                    block_size[++ block_num] = is_g[k];
                    block_id[k] = block_num; 
                    block[block_num].pb(k);
                    int &t = block_size[block_num];
                    while(stk.top() != k){
                            t += is_g[stk.top()]; 
                            block[block_num].pb(stk.top());
                            block_id[stk.top()] = block_num;
                            In_stk[stk.top()] = 0; stk.pop();
                    }
                    In_stk[stk.top()] = 0;stk.pop();
            }
    }
    
    void Out_line(int k, int b_id){
            for(reg int i = head[k]; i; i = edge[i].nxt){
                    int to = edge[i].to;
                    if(b_id == block_id[to]) continue ;
                    if(!sete[b_id].count(block_id[to])){
                            Add_2(b_id, block_id[to]);
                            sete[b_id].insert(block_id[to]);
                    }
            }
    }
    
    void DFS(int k){
            if(Ans[k]) return ; 
            Ans[k] = block_size[k];
            for(reg int i = head_2[k]; i; i = edge_2[i].nxt){
                    int to = edge_2[i].to;
                    DFS(to); Ans[k] += Ans[to];
            }
    }
    
    int main(){
            scanf("%d", &N);
            for(reg int i = 1; i <= N; i ++) X[i] = read(), R[i] = read();
            Build(rot[0], 1, N, 0), Build(rot[1], 1, N, 1);
            Connect_0(rot[0]);
            for(reg int i = 1; i <= N; i ++){
                    int l_lim = std::lower_bound(X+1, X+N+1, X[i]-R[i]) - X;
                    int r_lim = std::upper_bound(X+1, X+N+1, X[i]+R[i]) - X-1;
                    Connect_1(rot[1], l_lim, r_lim, i);
            }
            for(reg int i = 1; i <= N; i ++){
                    int id = Mp[0][i];
                    if(!dfn[id]) Tarjan(id);
            }
            for(reg int i = 1; i <= block_num; i ++){
                    int size = block[i].size();
                    for(reg int j = 0; j < size; j ++){ 
                            int id = block[i][j];
                            Out_line(id, i);
                    }
            }
            int ANS = 0;
            for(reg int i = 1; i <= block_num; i ++) DFS(i);
            for(reg int i = 1; i <= N; i ++) ANS = (1ll*ANS + (1ll*i*Ans[block_id[Mp[0][i]]]%mod)) % mod;
            printf("%d
    ", ANS);
            return 0;
    }
    
    
  • 相关阅读:
    最短路径算法
    XMLhelper
    关于NuDaqPci 数据采集
    批处理常用命令及用法大全
    c#智能感知(设置)及实现
    单片机串行通信全解析
    Esp8266
    使用NOOBS给树莓派安装系统Raspbian
    命令提示符编译java
    javaWeb使用POI操作Excel
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822549.html
Copyright © 2011-2022 走看看