「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;
}

时间: 2024-10-12 22:06:17

「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)的相关文章

「6月雅礼集训 2017 Day4」暴力大神hxx

[题目大意] 给出一个n重循环,每重循环有范围$[l, r]$,其中$l$,$r$可能是之前的变量,也可能是常数.求循环最底层被执行了多少次. 其中,保证每个循环的$l$,$r$最多有一个是之前的变量.设所有常数最大值为C. $1 \leq n \leq 26, 1\leq C \leq 10^5$ [题解] 发现构成了一个森林. 树形dp,稍微推一些循环交换顺序等等的性质,然后乘在一起就行了. 大概设f[x,i]表示到了x这个点,x的取值为i,x的子树的执行次数. 转移用个前缀和优化即可.复杂

「6月雅礼集训 2017 Day10」quote

[题目大意] 一个合法的引号序列是空串:如果引号序列合法,那么在两边加上同一个引号也合法:或是把两个合法的引号序列拼起来也是合法的. 求长度为$n$,字符集大小为$k$的合法引号序列的个数.多组数据. $1 \leq T \leq 10^5, 1 \leq n \leq 10^7, 1\leq K \leq 10^9$ [题解] 显然引号序列可以看做括号序列,于是我们有了一个$O(n^2)$的dp了. 设$f_{i,j}$表示到第$i$个位置,前面有$j$个左引号没有匹配,的方案数 每次,要么有

「6月雅礼集训 2017 Day7」电报

[题目大意] 有n个岛屿,第i个岛屿有有向发射站到第$p_i$个岛屿,改变到任意其他岛屿需要花费$c_i$的代价,求使得所有岛屿直接或间接联通的最小代价. $1 \leq n \leq 10^5, 1 \leq p_i,c_i \leq 10^9$ [题解] 显然最后是个大环,特判原来就是大环的情况. 考虑每个连通块最多保留多少. 树的答案可以直接dp做出来. 环的答案,根据树的答案dp出来. h[x][0/1]表示当前做到环上第i个点,环是否被切断了,的最大保留价值. 因为环必须被切断一次.所

「6月雅礼集训 2017 Day8」gcd

[题目大意] 定义times(a, b)表示用辗转相除计算a和b的最大公约数所需步骤. 那么有: 1. times(a, b) = times(b, a) 2. times(a, 0) = 0 3. times(a, b) = times(b, a mod b) + 1 对于$1 \leq x \leq A, 1 \leq y \leq B$,求times(A, B)的最大值,以及有多少对数取到了最大值. 多组数据. $T \leq 3 \times 10^5, 1 \leq A,B \leq

「6月雅礼集训 2017 Day7」三明治

[题目大意] $1 \leq n,m \leq 400$ N字形表示:上图第1行第1个那种:Z字形表示上图第1行第2个那种. [题解] 很容易得到结论: 考虑如果紫色比绿色先消去,那么黄色一定会比对应的白色先消去(这样才能消去白色). 然后我们可以知道,设取走$(x, y)$靠左的正方形,要$L(x,y)$步,那么$L(x,y) \geq L(x,y-1)$,同理也有$L(x, y) \geq L(x, y+1)$. 然后我们对于这个就可以进行一行一行的dfs了,dfs的时候可能会搜到原来已经搜

「6月雅礼集训 2017 Day5」吃干饭

[题目大意] 询问[L,R]中选若干个数异或起来得到的答案集合大小.多组数据. 对于50%的数据,$R - L \leq 10^4$ 对于100%的数据,$R - L \leq 10^{18}, T \leq 10^2$ [题解] 考虑50%的数据,暴力线性基即可. 这样的复杂度是$O(T(R-L)logn)$ 观察到数据比较特殊,是连续的一段正整数,我们写完线性基暴力然后打个表观察数在什么时候被插入到线性基里. 我们以[23333, 66666]为例: 首先L=23333,这个数一定被插入到线

「6月雅礼集训 2017 Day10」perm(CodeForces 698F)

[题目大意] 给出一个$n$个数的序列$\{a_n\}$,其中有些地方的数为0,要求你把这个序列填成一个1到$n$的排列,使得: $(a_i, a_j) = 1$,当且仅当$(i, j) = 1$.多组数据. $n \leq 3\times 10^5, T\leq 10$ CodeForces:无多组数据,$n \leq 10^6$ [题解] 这题有点神奇啊.. 首先考虑序列全是0要怎么做. 考虑到如果两个数的位置含有的因数种类完全一样,那么它们是可以互换的.(这个挺显然的) 观察如果两个质数的

「6月雅礼集训 2017 Day2」A

[题目大意] 给出一棵树,求有多少对点(u,v)满足其路径上不存在两个点a,b满足(a,b)=1 n<=10^5 [题解] 考虑找出所有不符合的点对,共有n*ln(n)对,他们要么是祖先->儿子边,要么是不是. 考虑祖先->儿子边,那么一个点在祖先以上,一个点在儿子以下的点对全部无法访问. 考虑另外一种边,就是LCA不是两个端点的,这就比较好统计了,两个点在这两棵子树的点对无法访问. 考虑用DFS序,这样子树就是连续的一段(祖先以上是连续两段) 然后就是一个二维覆盖问题,用扫描线+线段树

「6月雅礼集训 2017 Day8」infection

[题目大意] 有$n$个人,每个人有一个初始位置$x_i$和一个速度$v_i$,你需要选择若干个人来感染一个傻逼病毒. 当两个人相遇(可以是正面和背面),傻逼病毒会传染,求经过无限大时间后,传染完所有人的方案数. [题解] 考虑经过无限大时间后结束的数列,一定是按$v_i$排序的. 对于第i个人,如果他带有病毒,那么 原来在它左边的速度最大的点一定会超过它,到达右边能到达的最大值,这个点会经过若干个点,这些都会被传染. 原来在它右边的速度最小的点一定会跑到它的后面,到达左边能到达的最小值,同理也