• 「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)


    原题传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2687

    【题目大意】

    给出若干区间,求一个区间的大于等于2的子集,使得 |区间并| 和 |区间交| 的乘积最大。

    $1leq n,L_i,R_ileq 10^6$

    【题解】

    把区间去掉包含情况,然后进行排序,变成$l_i$和$r_i$都递增的数列。

    然后容易发现取得区间一定是连续的一段。

    然后我们推一推决策单调性。

    容易得出当$j$优于$k$的情况:

    $r_i * (r_j - r_k) + l_i * (l_j - l_k) > l_j * r_j - l_k * r_k$

    当$i$变化成$i+1$的时候,若$j > k$,那么如下情况还成立。

    说明当$i$往右的时候,最优决策点不会往左。

    然后要注意的是,还有一种情况,也就是包含的情况需要讨论。

    我的做法可能比较奇怪,包含的情况,对于每个被包含的区间,贡献最大值是包含它的长度最长的区间。

    我们对左端点进行排序,发现要找的包含的一定是右端点大于当前讨论区间的右端点(左端点已经固定小于了),的最长区间。

    我们对于区间长度建一棵线段树,然后线段树维护长度在区间内的右端点max,询问相当于在线段树上二分,复杂度$O(logn)$。

    现在来讨论有了决策单调性要怎么办

    1. 维护一个i递增,答案递减的的单调队列。

    2. 二分决策

    考虑第一种方案,当$i$右移的时候,前面一个单调下降函数,不一定会变成一个单峰或单调下降的函数,可能是有很多峰值,我们当前爬
    上的这个峰不是最优解。

    网上基本上所有单调队列代码都是错的(我只看到一份对的),包括我之前发的那份文章也是错的。可以被这个数据卡掉:

    5
    0 100
    10 105
    20 112
    25 115
    30 140

    最优解是选择[20,112], [25,115], [30,140](中间的那个区间可以选可以不选)。

    可是由于我们的单调队列的设定,左端点会一直停留在[0,100]这个区间,实际上后面的[20,112]这个区间比[0,100]更优。

    所以只能二分决策了。。

    二分的时候有个技巧,就是可以按照类似于整体二分的思路。

    定义solve(l, r, al, ar)

    表示目前处理[l,r]之间的转移,决策点在[al, ar]之间。

    然后每次暴力找出mid的时候的决策即可。

    # include <queue>
    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ld;
    
    const int M = 2e5 + 10, N = 1e6 + 10, F = 1e6;
    const int mod = 1e9 + 7;
    const ll inf = 1e15;
    
    inline int getint() {
        int x = 0; char ch = getchar();
        while(!isdigit(ch)) ch = getchar();
        while(isdigit(ch)) {
            x = (x<<3) + (x<<1) + ch - '0';
            ch = getchar();
        }
        return x;
    }
    
    int n, q[N];
    ll ans;
    struct pa {
        int l, r;
        pa() {}
        pa(int l, int r) : l(l), r(r) {}
        friend bool operator < (pa a, pa b) {
            return a.l < b.l || (a.l == b.l && a.r > b.r);
        }
    }p[N], t[N]; int tn = 0;
    
    inline bool cmp(pa a, pa b) {
        return a.l < b.l || (a.l == b.l && a.r < b.r);
    } 
    
    inline ll gsum(int i, int j) {
        return (ll)(p[i].r-p[j].l) * (ll)(p[j].r-p[i].l);
    }
    
    int RM[N];
    
    struct SMT { 
        # define ls (x<<1)
        # define rs (x<<1|1)
        int w[N << 2];
        inline void set() {
            memset(w, 0, sizeof w);
        }
        inline void edt(int x, int l, int r, int pos, int d) {
            if(l == r) {
                w[x] = max(w[x], d);
                return ;
            }
            int mid = l+r>>1;
            if(pos <= mid) edt(ls, l, mid, pos, d);
            else edt(rs, mid+1, r, pos, d);
            w[x] = max(w[ls], w[rs]);
        }
        inline int gs(int x, int l, int r, int R) {
            if(w[x] < R) return 0;
            if(l == r) return l;
            int mid = l+r>>1;
            if(w[rs] >= R) return gs(rs, mid+1, r, R);
            else return gs(ls, l, mid, R);
        }
    }T;
    
    inline void solve(int l, int r, int al, int ar) {
        if(l > r) return ;
        int pos = 0, mid = l+r>>1; ll mx = -1e15, t;
        for (int i=al; i<=ar && i<mid; ++i) 
            if((t = gsum(mid, i)) > mx) mx = t, pos = i;
        if(pos) ans = max(ans, gsum(mid, pos));
        solve(l, mid-1, al, pos);
        solve(mid+1, r, pos, ar);
    }
    
    int main() {
        ll tmp; T.set();
        n = getint();
        for (int i=1; i<=n; ++i) p[i].l = getint(), p[i].r = getint();
        sort(p+1, p+n+1);
        t[tn = 1] = p[1]; T.edt(1, 0, F, p[1].r - p[1].l, p[1].r);
        int mxr = p[1].r;
        for (int i=2; i<=n; ++i) {
            if(p[i].r <= mxr) {
                tmp = T.gs(1, 0, F, p[i].r);
                tmp = tmp * (p[i].r - p[i].l);
                if(tmp > ans) ans = tmp;
                continue;
            }
            t[++tn] = p[i]; 
            T.edt(1, 0, F, p[i].r-p[i].l, p[i].r);
            mxr = p[i].r;
        }
        
        n = tn;
        for (int i=1; i<=n; ++i) p[i] = t[i];
        
        solve(1, n, 1, n);
        
        cout << ans << endl;
        
        return 0;
    }
    /*
    5
    0 100
    10 105
    20 112
    25 115
    30 140
    */
    View Code

    下面那份是考场写的,过了但是有问题,可以被卡掉(感谢chrt给我了一个提醒,发现自己代码是错的qwq)

    # include <queue>
    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ld;
    
    const int M = 2e5 + 10, N = 1e6 + 10, F = 1e6;
    const int mod = 1e9 + 7;
    
    inline int getint() {
        int x = 0; char ch = getchar();
        while(!isdigit(ch)) ch = getchar();
        while(isdigit(ch)) {
            x = (x<<3) + (x<<1) + ch - '0';
            ch = getchar();
        }
        return x;
    }
    
    int n, q[N];
    struct pa {
        int l, r;
        pa() {}
        pa(int l, int r) : l(l), r(r) {}
        friend bool operator < (pa a, pa b) {
            return a.l < b.l || (a.l == b.l && a.r > b.r);
        }
    }p[N], t[N]; int tn = 0;
    
    inline bool cmp(pa a, pa b) {
        return a.l < b.l || (a.l == b.l && a.r < b.r);
    } 
    
    /*
    inline int gs(int x) {
        int l = 1, r = n, mid;
        while(1) {
            if(r-l <= 3) {
                for (int i=l; i<=r; ++i) 
                    if(p[i].r > x) return i;
                return -1;
            }
            mid = l+r>>1;
            if(p[mid].r > x) r = mid;
            else l = mid;
        }
        return -1;
    }
    */
    
    inline ll gsum(int i, int j) {
        return (ll)(p[i].r-p[j].l) * (ll)(p[j].r-p[i].l);
    }
    
    int RM[N];
    
    struct SMT { 
        # define ls (x<<1)
        # define rs (x<<1|1)
        int w[N << 2];
        inline void set() {
            memset(w, 0, sizeof w);
        }
        inline void edt(int x, int l, int r, int pos, int d) {
            if(l == r) {
                w[x] = max(w[x], d);
                return ;
            }
            int mid = l+r>>1;
            if(pos <= mid) edt(ls, l, mid, pos, d);
            else edt(rs, mid+1, r, pos, d);
            w[x] = max(w[ls], w[rs]);
        }
        inline int gs(int x, int l, int r, int R) {
            if(w[x] < R) return 0;
            if(l == r) return l;
            int mid = l+r>>1;
            if(w[rs] >= R) return gs(rs, mid+1, r, R);
            else return gs(ls, l, mid, R);
        }
    }T;
    
    // # include <time.h>
    
    int main() {
    //    int tm = clock();
        freopen("qyh.in", "r", stdin);
        freopen("qyh.out", "w", stdout);
        ll ans = 0, tmp; T.set();
    //    cin >> n; 
        n = getint();
    //    for (int i=1; i<=n; ++i) scanf("%d%d", &p[i].l, &p[i].r);
        for (int i=1; i<=n; ++i) p[i].l = getint(), p[i].r = getint();
        sort(p+1, p+n+1);
        t[tn = 1] = p[1]; T.edt(1, 0, F, p[1].r - p[1].l, p[1].r);
        int mxr = p[1].r;
        for (int i=2; i<=n; ++i) {
            if(p[i].r <= mxr) {
                tmp = T.gs(1, 0, F, p[i].r);
                tmp = tmp * (p[i].r - p[i].l);
                if(tmp > ans) ans = tmp;
                continue;
            }
            t[++tn] = p[i]; 
            T.edt(1, 0, F, p[i].r-p[i].l, p[i].r);
            mxr = p[i].r;
        }
        
        n = tn;
        for (int i=1; i<=n; ++i) p[i] = t[i];
    //    for (int i=1; i<=n; ++i) printf("%d %d
    ", p[i].l, p[i].r);
        
        int lst = 1, head = 1, tail = 0;
        for (int i=2; i<=n; ++i) {
            /* (r_i - l_j) * (r_j - l_i)
               r_i * r_j + l_i * l_j - l_j * r_j - l_i * r_i   max
               if   (r_i - l_j) * (r_j - l_i) > (r_i - l_k) * (r_k - l_i)
               r_i * r_j + l_i * l_j - l_j * r_j > r_i * r_k + l_i * l_k - l_k * r_k
               r_i * (r_j - r_k) + l_i * (l_j - l_k) > l_j * r_j - l_k * r_k
    
               suppose j>k and j is better than k
               if i + 1, then r_{i+1} * (r_j - r_k) + l_{i+1} * (l_j - l_k) > l_j * r_j - l_k * r_k
            
               suppose j<k and j is better than k
               if i + 1, then r_{i+1} * (r_j - r_k) + l_{i+1} * (l_j - l_k) > l_j * r_j - l_k * r_k
            */
            
            /*
            if(gsum(i, i-1) > gsum(i, lst)) lst = i-1;
            tmp = gsum(i, lst);
    //        cout << lst << endl;
            if(tmp > ans) ans = tmp;
            */
            while(head < tail && gsum(i, q[head]) < gsum(i, q[head+1])) ++head;
            while(head <= tail && gsum(i, i-1) >= gsum(i, q[tail])) --tail;
            q[++tail] = i-1;
            if(head <= tail) {
    //            printf("%d %d
    ", i, q[head]);
                tmp = gsum(i, q[head]);
                if(tmp > ans) ans = tmp;
            }
        }
        
        cout << ans;
    
    //    cerr << clock() - tm << " ms" << endl;
    
        return 0;
    }
    View Code

     网上还有某种双指针做法,已经被cha掉了

    4
    1 301000
    300990 301001
    300991 301002
    300992 500000

    答案:3999992

  • 相关阅读:
    权限、角色及架构 【转载】
    C#和SQL SERVER 实用工具推荐 『转载』
    创建自定义配置节点(web.config和app.config都适用)【转载】
    asp.net代码中尖括号和百分号的含义 <转载>
    为JavaScript程序添加客户端不可见的注释 【转载】
    C#中string[]数组和list<string>泛型的相互转换 【转】
    程序员从初级到中级10个秘诀 【转载】
    PowerDesigner 使用教程 【转载】
    【openstack N版】——网络服务neutron(flat扁平网络)
    【openstack N版】——镜像服务glance
  • 原文地址:https://www.cnblogs.com/galaxies/p/20170620_b.html
Copyright © 2020-2023  润新知