首页 > 代码库 > 「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)

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

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

【题目大意】

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

$1\leq n, L_i, R_i \leq 10^6$

【题解】

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

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

然后我们推一推决策单调性,我把考试的时候推的写在代码里了

然后直接单调队列优化即可。

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

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

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

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

总复杂度$O(nlogn)$,竟然没有被卡常23333

代码似乎写的非常丑。。。

技术分享
# 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\n", 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\n", i, q[head]);
            tmp = gsum(i, q[head]);
            if(tmp > ans) ans = tmp;
        }
    }
    
    cout << ans;

//    cerr << clock() - tm << " ms" << endl;

    return 0;
}
View Code

 

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