[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]区间。

扫描二维码关注公众号,回复: 8473730 查看本文章

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

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

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

可以用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\n", xi, yi);
153         }else{
154             int ai = DL.MIN(xi, yi - 1);
155             int bi = DR.MAX(xi, yi - 1);
156             printf("%d %d\n", 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\n", 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\n", aL, bR);
    }
    return 0;
}
View Code

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

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

猜你喜欢

转载自www.cnblogs.com/hjj1871984569/p/12172879.html