• [CERC 2017] Intrinsic Interval


    题目:Intrinsic Interval

    链接:https://codeforces.com/gym/101620

    题意:

    Intrinsic Interval : 排序后是连续区间

    给定区间(a,b),求包含该区间的的最大的Intrinsic Interval

    分析:

    1)参考:https://www.cnblogs.com/yqgAKIOI/p/10087038.html

    平凡区间(即区间长度为1的区间)都是Intrinsic Interval的,这种做法的本质是联系,构造依赖。

    如果我们要把两个相邻区间合并到一起,左边区间最右边的数和右边区间最左边的数取到决定作用。

    我们分析最特殊的区间,两个相邻的平凡区间合并[i,i+1],这表示[ a[i],a[i+1] ] 中所有的权值都要出现。

    这些权值出现的最左和最右位置卡出来的区间 [l,r]是[i,i+1]的依赖,也就是为了合并[i, i+1],至少要合并[l,r]区间。

    合并一个区间就是把区间中两两相邻区间合并。

    向区间连边问题可以用线段树辅助建图。

    这样就得到了一张依赖网络,有向图。

    可以用tarjan求SCC,然后缩点重构图,可以求得每一个包含[i,i+1]的最小Intrinsic Interval 。

    从析合树来看,就是在求析点和相邻的合点。

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 const int INF = 1e9;
      4 const int MAXN = 5e5 + 7;
      5 int a[MAXN], b[MAXN], dl[MAXN], dr[MAXN];
      6 int dfs_clock, scc_cnt, dfn[MAXN], low[MAXN], sccno[MAXN];
      7  
      8 struct RMQ{
      9     static const int S = 20;
     10     int lg[MAXN], mx[MAXN][S], mn[MAXN][S];
     11     void init(int *a, int n) {
     12         for(int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
     13         for(int i = 1; i <= n; i++) mn[i][0] = mx[i][0] = a[i];
     14         for(int k = 1; (1 << k) <= n; k++) 
     15             for(int i = 1; i + (1 << k) - 1 <= n; i++) {
     16                 mn[i][k] = min(mn[i][k - 1], mn[i + (1 << (k - 1))][k - 1]);
     17                 mx[i][k] = max(mx[i][k - 1], mx[i + (1 << (k - 1))][k - 1]);
     18             }
     19     }
     20     inline int MIN(int l, int r) {
     21         int len = lg[r - l + 1];
     22         return min(mn[l][len], mn[r - (1 << len) + 1][len]);
     23     }
     24     inline int MAX(int l, int r) {
     25         int len = lg[r - l + 1];
     26         return max(mx[l][len], mx[r - (1 << len) + 1][len]);
     27     }
     28 } D, DL, DR;
     29 namespace SCC{
     30     vector<int> G[MAXN];
     31     int vis[MAXN];
     32     vector<int> G2[MAXN];
     33     void addEdge(int u, int v) {
     34         G[u].push_back(v);
     35     }
     36     int id[MAXN];
     37     struct SEG{
     38         int setL, setR, W;
     39         int L[MAXN], R[MAXN];
     40         void init(int v, int l, int r) {
     41             L[v] = l; R[v] = r + 1;
     42             if(l == r) {id[l] = v; return;}
     43             int mid = (l + r) >> 1;
     44             addEdge(v, v << 1);
     45             addEdge(v, v<<1|1);
     46             init(v << 1, l, mid);
     47             init(v<<1|1, mid+1, r);
     48         }
     49         void init2(int v, int l, int r) {
     50             for(auto u : G[v]) {
     51                 if(sccno[v] != sccno[u]) {
     52                     G2[sccno[v]].push_back(sccno[u]);
     53                 }
     54             }
     55             if(l == r) return;
     56             int mid =(l + r) >> 1;
     57             init2(v << 1, l, mid);
     58             init2(v<<1|1, mid+1, r);
     59         }
     60         void add(int v, int l, int r) {
     61             if(setL <= l && r <= setR) {
     62                 addEdge(W, v);
     63                 return;
     64             }
     65             int mid = (l + r) >> 1;
     66             if(setL <= mid) add(v << 1, l, mid);
     67             if(mid < setR) add(v<<1|1, mid + 1, r);
     68         }
     69     }T;
     70     stack<int> S;
     71     int sl[MAXN], sr[MAXN];
     72     void tarjan_init(int n) {
     73         dfs_clock = scc_cnt = 0;
     74         for(int i = 0; i <= n; i++) {
     75             dfn[i] = low[i] = sccno[i] = 0;
     76             G[i].clear();
     77         }
     78         T.init(1, 1, n);
     79         for(int i = 1; i < n; i++) {
     80             int ai = a[i], aj = a[i+1];
     81             if(ai > aj) swap(ai, aj);
     82             T.setL = D.MIN(ai, aj);
     83             T.setR = D.MAX(ai, aj);
     84             if(T.setL > T.setR) swap(T.setL, T.setR);
     85             --T.setR;
     86             T.W = id[i];
     87             T.add(1, 1, n);
     88         }
     89     }
     90     void tarjan(int u) {
     91         dfn[u] = low[u] = ++dfs_clock;
     92         S.push(u);
     93         for(auto v : G[u]) {
     94             if(!dfn[v]) {
     95                 tarjan(v);
     96                 low[u] = min(low[u], low[v]);
     97             }else if(!sccno[v]) {
     98                 low[u] = min(low[u], dfn[v]);
     99             }
    100         }
    101         if(dfn[u] == low[u]) {
    102             scc_cnt++;
    103             sl[scc_cnt] = INF;
    104             sr[scc_cnt] = -INF;
    105             for(;;) {
    106                 int v = S.top(); S.pop();
    107                 sccno[v] = scc_cnt;
    108                 sl[scc_cnt] = min(sl[scc_cnt], T.L[v]);
    109                 sr[scc_cnt] = max(sr[scc_cnt], T.R[v]);
    110                 if(v == u) break;
    111             }
    112         }
    113     }
    114     void dfs(int u) {
    115         vis[u] = 1;
    116         for(auto v : G2[u]) {
    117             if(!vis[v]) dfs(v);
    118             sl[u] = min(sl[u], sl[v]);
    119             sr[u] = max(sr[u], sr[v]);
    120         }
    121     }
    122     void sol(int n) {
    123         for(int i = 1; i < n; i++) 
    124             if(!dfn[id[i]]) tarjan(id[i]);
    125         T.init2(1, 1, n);
    126         for(int i = 1; i <= scc_cnt; i++) 
    127             if(!vis[i]) dfs(i);
    128         for(int i = 1; i < n; i++) {
    129             dl[i] = sl[sccno[id[i]]];
    130             dr[i] = sr[sccno[id[i]]];
    131         }
    132     }
    133 }
    134 int main() {
    135     int n;
    136     scanf("%d", &n);
    137     for(int i = 1; i <= n; i++) {
    138         scanf("%d", a + i);
    139         b[a[i]] = i;
    140     }
    141     D.init(b, n);
    142     SCC::tarjan_init(n);
    143     SCC::sol(n);
    144     
    145     DL.init(dl, n);
    146     DR.init(dr, n);
    147     int m;
    148     scanf("%d", &m);
    149     for(int i = 1, xi, yi; i <= m; i ++) {
    150         scanf("%d%d", &xi, &yi);
    151         if(xi == yi) {
    152             printf("%d %d
    ", xi, yi);
    153         }else{
    154             int ai = DL.MIN(xi, yi - 1);
    155             int bi = DR.MAX(xi, yi - 1);
    156             printf("%d %d
    ", ai, bi);
    157         }
    158     }
    159     return 0;
    160 } 
    View Code

    2)

    参考:https://www.luogu.com.cn/blog/ywycasm/solution-p4747

    Intrinsic Interval 还有一种表述:

    定义(i,j)为一个好的二元组,当且仅当a[i]-a[j]=1

    这样的两项的二元组在[l,r]中恰好有r-l个

    所以,一个区间是好的区间,当且仅当好的二元组有r-l个

    也就是 val + l = r, 其中val是区间[l,r]中好二元组的个数

    枚举r,在[1,l]中如果存在 val + i = r, 最靠右的i就是答案

    上面算式可以用线段树维护

    如果 $$ a[r] > 1 && pos[a[r] - 1] < r $$ 这样[1,pos[a[r] -1] ] 就会多一个整数对。

    如果 $$ a[r] < n && pos[a[r] + 1] < r $$ 这样[1,pos[a[r] + 1] ] 就会多一个整数对。

    这个本质是将难以维护的问题转化为可维护的计数问题。

    这种做法需要离线

    #include <bits/stdc++.h>
    using namespace std;
    typedef pair<int, int> P;
    const int MAXN = 100001;
    int a[MAXN], b[MAXN];
    P ans[MAXN];
    struct que{
        int l, r, id;
        bool operator < (const que &x) const {
            if(l == x.l && r == x.r) return id < x.id;
            if(l == x.l) return r < x.r;
            return l > x.l;
        } 
    } q[MAXN];
    struct SGT{
        int setL, setR, setW;
        P val[MAXN << 2];
        int tag[MAXN << 2];
        void push_down(int v) {
            if(tag[v]) {
                val[v << 1].first += tag[v]; 
                tag[v << 1] += tag[v];
                val[v<<1|1].first += tag[v];
                tag[v<<1|1] += tag[v];
                tag[v] = 0;
            }
        }
        void push_up(int v) {
            val[v] = max(val[v << 1] , val[v<<1|1]);
        }
        void init(int v, int l, int r) {
            val[v] = {l, l};
            tag[v] = 0;
            if(l == r) return;
            int mid = (l + r) >> 1;
            init(v<<1, l, mid);
            init(v<<1|1, mid + 1, r);
            push_up(v);
        }
        void upt(int v, int l, int r) {
            if(setL <= l && r <= setR) {
                val[v].first += setW;
                tag[v] += setW;
                return;
            }
            push_down(v);
            int mid = (l + r) >> 1;
            if(setL <= mid) upt(v<<1, l, mid);
            if(mid < setR ) upt(v<<1|1, mid+1,r);
            push_up(v);
        }
        P que(int v, int l, int r) {
            if(setL <= l && r <= setR) {
                return val[v];
            } 
            push_down(v);
            int mid = (l + r) >> 1;
            P res = {0, 0};
            if(setL <= mid) res = max(res, que(v << 1, l, mid));
            if(mid < setR ) res = max(res, que(v<<1|1, mid+1, r));
            return res;
        }
    }T;
    int main() {
        int n;
        scanf("%d", &n);
        T.init(1, 1, n);
        for(int i = 1; i <= n; i++) {
            scanf("%d", a + i);
            b[a[i]] = i;
        }
        int m;
        scanf("%d", &m);
        for(int i = 1; i <= m; i++) {
            scanf("%d%d", &q[i].l, &q[i].r);
            q[i].id = i;
        }
        sort(q + 1, q + m + 1, [=] (const que &x, const que &y) {
            if (x.l == y.l && x.r == y.r) return x.id < y.id;
            if (x.r == y.r) return x.l < y.l;
            return x.r < y.r;
        });
        set <que> st;
        for(int i = 1, j = 1; i <= n; i++) {
            while(j <= m && q[j].r == i) {
                st.insert(q[j]);
                ++j;
            }
            if(a[i] > 1 && b[a[i] - 1] < i) {
                T.setL = 1; T.setR = b[a[i] - 1]; T.setW = 1;
                T.upt(1, 1, n);
            }
            if(a[i] < n && b[a[i] + 1] < i) {
                T.setL = 1; T.setR = b[a[i] + 1]; T.setW = 1;
                T.upt(1, 1, n);
            }
            while(st.size()) {
                auto it = st.begin();
                T.setL = 1; T.setR = it->l;
                P mx = T.que(1, 1, n);
                if(mx.first != i) break;
                else {
                    ans[it->id].first = mx.second; 
                    ans[it->id].second = i;
                    st.erase(it);
                }
            }
        }
        for(int i = 1; i <= m; i++) 
            printf("%d %d
    ", ans[i].first, ans[i].second);
        return 0;
    }
    View Code

    3)

    参考:https://oi-wiki.org/ds/divide-combine/

    区间最大值-区间最小值=区间长度

    也就是: mx-mn = r - l, 令 fx = (mx-mn) - (r - l)

    析合树利用了单调栈维护了最大值最小值的断点位置,用线段树维护上式

    在析合树上求lca就可以了

    #include <bits/stdc++.h>
    using namespace std;
     
    const int MAXN = 200010;
    const int S = 22;
    typedef long long LL;
     
    namespace CST{
        struct RMQ {
            int lg[MAXN], mn[MAXN][S+1], mx[MAXN][S+1];
            inline void init(int *a, int n) {
                for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
                for (int i = 1; i <= n; i++) mn[i][0] = mx[i][0] = a[i];
                for (int k = 1; (1 << k) <= n; k++)
                    for (int i = 1; i + (1 << k) - 1 <= n; i++) {
                        mn[i][k] = min(mn[i][k - 1], mn[i + (1 << (k - 1))][k - 1]);
                        mx[i][k] = max(mx[i][k - 1], mx[i + (1 << (k - 1))][k - 1]);
                    }
            }
            inline int Min(int l, int r) {
                int len = lg[r - l + 1];
                return min(mn[l][len], mn[r - (1 << len) + 1][len]);
            }
            inline int Max(int l, int r) {
                int len = lg[r - l + 1];
                return max(mx[l][len], mx[r - (1 << len) + 1][len]);
            }
        } D;
     
        struct SEG {
            int setL, setR, setW;
            int mn[MAXN << 2], tag[MAXN << 2];
            
            inline void pushup(int x) {
                mn[x] = min(mn[x << 1], mn[x << 1 | 1]);
            }
            inline void pushdown(int x) {
                if(!tag[x]) return;
                mn[x << 1] += tag[x]; mn[x << 1 | 1] += tag[x];
                tag[x << 1] += tag[x]; tag[x << 1 | 1] += tag[x]; tag[x] = 0;
            }
            void init(int x, int l, int r) {
                mn[x] = tag[x] = 0;
                if (l == r) return;
                int mid = (l + r) >> 1;
                init(x << 1, l, mid);
                init(x << 1 | 1, mid + 1, r);
            }
            void upt(int x, int l, int r) {
                if (setL <= l && r <= setR) {
                    tag[x] += setW; mn[x] += setW; 
                    return;
                }
                pushdown(x);
                int mid = (l + r) >> 1;
                if (setL <= mid) upt(x << 1, l, mid);
                if (mid < setR ) upt(x << 1 | 1, mid+1, r);
                pushup(x);
            }
            int que(int x, int l, int r) {
                if (l == r) return l;
                pushdown(x);
                int mid = (l+r)>>1;
                if (!mn[x << 1]) return que(x << 1, l, mid);
                return que(x << 1 | 1, mid+1, r);
            }
        } T;
     
        int tpmn, stmn[MAXN], tpmx, stmx[MAXN], tpk, stk[MAXN];
        int ncnt, type[MAXN<<1], L[MAXN<<1], R[MAXN<<1], M[MAXN<<1];
        int dep[MAXN<<1], fa[MAXN<<1][S+1], C[MAXN<<1];
        int id[MAXN << 1];
        int newnode(int _type, int _L, int _R, int _M = 0) {
            ++ncnt; type[ncnt] = _type; 
            L[ncnt] = _L; R[ncnt] = _R; M[ncnt] = _M;
            C[ncnt] = 0;
            return ncnt;
        }
        
        inline bool judge(int l, int r) {
            return D.Max(l, r) - D.Min(l, r) == r - l;
        }
        
        int ecnt, head[MAXN << 1];
        struct Edge{int to, nxt;} e[MAXN<<1];
        inline void addEdge(int x, int y) {
            e[++ecnt] = (Edge) {y, head[x]}; head[x] = ecnt;
            fa[y][0] = x; C[x]++;
        }
        void dfs(int u) {
            for(int j = 0; j < S; j++) fa[u][j+1] = fa[fa[u][j]][j];
            for(int i = head[u]; i; i = e[i].nxt) {
                dep[e[i].to] = dep[u] + 1;
                dfs(e[i].to);
            }
        }
        
        inline void init(int n) {
            ecnt = 0;
            for(int i = 0; i <= n; i++) head[i] = 0;
        }
        void buildT(int *a, int n) {
            init(n);
            D.init(a, n);
            T.init(1, 1, n);
            tpmn = tpmx = tpk = 0; 
            stmn[0] = stmx[0] = stk[0] = 0;
            for (int i = 1; i <= n; i++) {
                for (;tpmn && a[i] <= a[stmn[tpmn]]; --tpmn) {
                    T.setL = stmn[tpmn - 1] + 1; T.setR = stmn[tpmn]; T.setW = a[stmn[tpmn]];
                    T.upt(1, 1, n);
                }
                T.setL = stmn[tpmn] + 1; T.setR = i; T.setW = -a[i];
                T.upt(1, 1, n);
                stmn[++tpmn] = i;
                
                for (;tpmx && a[i] >= a[stmx[tpmx]]; --tpmx) {
                    T.setL = stmx[tpmx - 1] + 1; T.setR = stmx[tpmx]; T.setW = -a[stmx[tpmx]];
                    T.upt(1, 1, n);
                }
                T.setL = stmx[tpmx] + 1; T.setR = i; T.setW = a[i];
                T.upt(1, 1, n);
                stmx[++tpmx] = i;
                
                int Li = T.que(1, 1, n), np = id[i] = newnode(0, i, i), nq, nw; 
                while (tpk && L[nq = stk[tpk]] >= Li) {
                    if (type[nq] && judge(M[nq], i)) {
                        R[nq] = i;
                        addEdge(nq, np);
                        np = nq; tpk--;
                    } else if (judge(L[nq], i)) {
                        nw = newnode(1, L[nq], i, L[np]);
                        addEdge(nw, nq); addEdge(nw, np);
                        np = nw; tpk--;
                    } else {
                        nw = newnode(0, -1, i);
                        addEdge(nw, np);
                        do {
                            addEdge(nw, nq);
                            nq = stk[--tpk];
                        } while (tpk && !judge(L[nq], i));
                        addEdge(nw, nq);
                        L[nw] = L[nq]; R[nw] = i; 
                        np = nw; --tpk;
                    }
                }
                stk[++tpk] = np;
                T.setL = 1; T.setR = i; T.setW = -1;
                T.upt(1, 1, n);
            }
            assert(tpk == 1);
            dfs(stk[tpk]);
        }
        void lca(int u, int v, int &aL, int &bR) {
            if(u == v) {
                aL = L[u]; bR = R[v];
                return;
            }
            if(dep[u] > dep[v]) swap(u, v);
            for(int i = S; i >= 0; i--) 
                if(dep[fa[v][i]] >= dep[u]) v = fa[v][i];
            assert(u != v);
            for(int i = S; i >= 0; i--) 
                if(fa[u][i] != fa[v][i]) {
                    u = fa[u][i]; v = fa[v][i];
                }
            if(type[fa[u][0]]) {
                aL = min(L[v], L[u]);
                bR = max(R[v], R[u]);
            }else{
                aL = L[fa[u][0]];
                bR = R[fa[u][0]];
            }
        }
    };
     
    int a[MAXN];
    int main() {
        int n; scanf("%d", &n);
        for (int i = 1; i <= n; i++) 
            scanf("%d", &a[i]);
        CST::buildT(a, n);
        int q;
        scanf("%d", &q);
        while(q--) {
            int ai, bi, aL, bR;
            scanf("%d%d", &ai, &bi);
            CST::lca(CST::id[ai], CST::id[bi], aL, bR);
            printf("%d %d
    ", aL, bR);
        }
        return 0;
    }
    View Code

    第一种做法求scc和析合树本质都是在求本原连续段,但是析合树包含关系比较清楚,相对的,代码比较长

    第二种做法和析合树本质相同,是析合树的简化版,在扫描过程中就统计完毕,但是这样需要离线。

  • 相关阅读:
    MySQL/MariaDB 版本选择
    Linux查看某个进程的磁盘IO读写情况 pidstat
    Oracle 11gR2 Database UNDO表空间使用率居高不下处理
    Linux十字病毒查杀处理
    MySQL字符集与校对
    点与线、线与线之间的位置关系
    [向量] 点积应用-两个向量夹角
    点与线的距离及垂足点
    unity 4.6.1脚本解析出错,没有激活的勾,方法顺序出错
    Error building Player: Exception: Could not start java
  • 原文地址:https://www.cnblogs.com/hjj1871984569/p/12172879.html
Copyright © 2020-2023  润新知