2019 ICPC Xuzhou Regional I题 Interesting game 题解 【2019 徐州I题】

题目大意

A和B玩游戏。一开始有n个pair, . 分三步:
(1) B首先选m个pair删掉;
(2) A从剩下的pair中取走一个,假设取走, 他的收益是;
(3) B从剩下的pair中取走一个,假设取走, 他的收益是.
A想要越大越好,B想要越小越好。问双方采取最优策略情况下,对所有满足的m,的值是多少。

解法

为了便于思考,不妨将删去若干个pair理解成选出若干个pair,所以第一步变成B首先选m个pair作为游戏局面,此时. (我们称第一步结束后选出的pair为一个局面)

考虑第3步B的策略,B一定是从剩下的pair中选最大的。然后我们考虑第2步A的策略。A有2个策略:1. 将最大的pair取走. 2. 不取最大的pair,那么就要取剩下的pair中最大的。

如果我们将最大的pair记为, 除去pair剩下的pair中最大的记为, 除去pair剩下的pair中最大的记为。那么A选择策略1得到的答案是, 选择策略2得到的的答案是,于是最终答案是. 当时选择策略1, 否则选择策略2.

现在我们来讨论第一步B的策略。

对任何一个局面来说,由四个值就能确定答案. 所以我们可以称为这个局面的特征值。

而这四个值涉及到的pair不会超过3个(是一个pair,可能在同个pair,也不能不在)。也就是说,如果我们将这三个pair之外的其他pair,逐个去掉,答案也不会改变。这样一来,我们可以得到结论,假设时,B有一个最优策略,答案是ans,那么当时,B也可以构造出答案为ans的策略。(用刚才的方法逐个去掉pair)

可以看到,的情况比较特殊,我们特判掉。如何求时的答案呢?很简单,将所有pair按从大到小排序,枚举选出的第一个pair,那么剩下的那个pair在后面选,由于,所以答案是,于是从剩下的数中选最大的即可。

下面我们求的情况。

不妨将问题转出成,枚举,问最多能选出多少个pair,使得局面的特征值是。记为最多能选出的pair的个数。那么这道题就是,我们求出后,用更新的答案。(打个标记即可,最后做个后缀Min)

于是我们把问题从“大小为m的所有局面中求最优答案”转化成“对某个答案求局面大小m的最大值”. 但是, 有n个,有n个,也有n个,总的状态数是啊!说明很多状态不必枚举。

我们先考虑枚举第一维,. 不妨首先将所有pair按照从大到小排序,这样枚举时,剩余的所有pair都是在这个pair的后面中选(

固定后,从B的角度看,如果有得选择,肯定是希望越小越好,越大越好。那我们第二维枚举, 于是这时后面所有的的pair都能选择。注意到我们想让越大越好,所以肯定时把这个可以选择的pair()全部选了,这样能尽量的大,局面大小也能尽量的大。所有可以直接确定。于是我们只用枚举的状态,但仍然太多了。

我们不妨用个二维表格来分析这的状态。第i行表示枚举(注意数组已经按从大到小排好序了),第j列表示枚举的值为j.

单元格(i,j)的F值是1+“有多少个pair,满足”. 可见,对于每一行,从左到右,F值(最大的局面大小)是单调递增的。(随着上界的增加,能选的pair越来越多。)同时,对于每一行,从左到右,也是单调递增的,于是,对于每一行,一定是左边若干个单元格答案是策略1(答案是, 要求), 右边若干个单元格答案是策略2(答案是)

单元格里显示了答案是策略1还是策略2.

对于策略1的答案,由于从左到右会变大,所以会变小,所以就更优了。也就是说,在策略1的区域,对于同一行,如果我们从左往右移动,答案会变优,同时F值(最大的局面大小)也会变大。所以就没有必要考虑左边的状态了。也就是,在策略1的区域,同一行我们只需考虑最右的单元格。

对于策略2的答案, 对于同一列,固定,从上往下走,不断变小,所以答案不断变劣,同时F值会越来越小,于是同样地,在策略2的区域,对于同一列,只需考虑位置最上的那一个状态。
上图打勾的地方是需要计算的状态。

于是,我们就只需计算个状态的结果了,每一行要二分出策略1和策略2区域的分界点,而维护每个单元格对应的值,可以用个主席树。于是得到了复杂度 做法。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
const int INF = 1e9+7;
typedef pair<int,int> P;
#define x first
#define y second
P a[N];
int n;
int ans[N],numa[N],ida[N];
void Mini(int &a, int b) {
    if (b < a) a = b;
}
void Maxi(int &a, int b) {
    if (b > a) a = b;
}

struct Node {
    int cnt,chl,chr;
}T[N*21];
#define cnt(v) T[v].cnt
#define chl(v) T[v].chl
#define chr(v) T[v].chr
int tot;
void add(int &o, int last,int x, int l=0, int r=n) {
    o=++tot;
    T[o]=T[last];
    ++cnt(o);
    if (l+1==r) return;
    int mid=(l+r)/2;
    if (x < mid) add(chl(o), chl(last), x, l, mid);
    else add(chr(o), chr(last), x, mid, r);
}
int qcnt;
int query(int v1, int v2, int L, int R, int l=0, int r=n) { // [L,R)
    ++qcnt;
    if (R<=l || r<=L) return -1;
    if (cnt(v2)-cnt(v1)==0) return -1;
    if (l+1==r) return l;
    int mid=(l+r)/2;
    int q=query(chl(v1),chl(v2),L,R,l,mid);
    if (q!=-1) return q;
    return query(chr(v1),chr(v2),L,R,mid,r);
}
int rt[N];
vector<int> vec[N];
int sz;
inline int lowbit(int i) {
    return i&-i;
}
struct BIT {
    int dat[N];
    int sum(int i) {
        ++i;
        int res=0;
        for (; i > 0; i -= lowbit(i)) res+=dat[i];
        return res;
    }
    void add(int i, int x) {
        ++i;
        for (; i < N; i += lowbit(i)) dat[i] += x;
    }
}bit;
void update_ans(int m, int val) {
    if (m >= 3 && m <= n) Mini(ans[m],val);
}

bool test(int x, int i, int s) {
    int q=query(0,rt[x],i+1,n);
    if (q==-1) return false;
    return a[q].y>=s-numa[x];
}

int getpos(int i, int s) {
    int l=-1,r=sz;
    while (l+1<r) {
        int mid=(l+r)/2;
        if (test(mid,i,s)) r=mid;
        else l=mid;
    }
    return l;
}

int main() {
    scanf("%d",&n);
    for (int i = 0; i < n; i++) {
        scanf("%d%d", &a[i].x, &a[i].y);
        numa[i]=a[i].x;
    }
    fill(ans,ans+n+1,INF);
    sort(a,a+n, [](const P&i, const P&j) {
        return i.x+i.y<j.x+j.y;
    });
    int mxb=a[0].y;
    for (int i = 1; i < n; i++) {
        Mini(ans[2], a[i].x-mxb);
        Maxi(mxb, a[i].y);
    }
    sort(a,a+n, [](const P&i, const P&j) {
        return i.y>j.y;
    });
    sort(numa,numa+n);
    sz=unique(numa,numa+n)-numa;
    for (int i = 0; i < n; i++) {
        ida[i]=lower_bound(numa,numa+sz,a[i].x)-numa;
        vec[ida[i]].push_back(i);
        bit.add(ida[i],1);
    }
    for (int i = 0; i < sz; i++) {
        if (i > 0) rt[i]=rt[i-1];
        for (auto x: vec[i]) {
            add(rt[i],rt[i],x);
        }
    }
    int mxa=sz-1;
    for (int i=0; i < n; i++) {
        bit.add(ida[i],-1);
        int pos=getpos(i,a[i].x+a[i].y);
        if (pos>=0) {
            int q=query(0,rt[pos],i+1,n);
            if (q!=-1) {
                update_ans(1+bit.sum(pos),a[i].x-a[q].y);
            }
        }
        for (int x=mxa;x>pos&&x>=0;x--) {
            update_ans(1+bit.sum(x), numa[x]-a[i].y);
        }
        Mini(mxa,pos);
    }
    for (int i = n-1; i >= 3; i--) Mini(ans[i], ans[i+1]);
    for (int i=n;i>=2;i--) printf("%d\n",ans[i]);
}
全部评论

相关推荐

1 1 评论
分享
牛客网
牛客企业服务