牛客网 Popping Balloons 【线段树】

题意:在坐标轴上给你n个点(n<=1e5),点的横纵坐标是[0, 1e5],你可以从选3行,每行间隔是d,选3列同样间隔d,点只能被选一次,问你最多能选多少点。d<=1e5

思路:我们考虑对列建一棵线段树,每个结点放的是num[j] + num[j + d] + num[j + 2d] 的值,然后我们枚举行的同时删除选中的点,然后线段树查询最大值就行了。删除点的时候只会对自己x,x-d,x-2d的位置产生影响,删除过查询后要重新加上。

#include 
using namespace std;
typedef long long ll;
#define ls rt << 1
#define rs rt << 1|1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
const int N = 100005;
const int maxn = 2e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;
vector v[maxn];
int num[maxn]; //记录每列的数量
ll tree[maxn << 2];
int n, d;
 
void push_up(int rt)
{
    tree[rt] = max(tree[ls], tree[rs]);
}
void build(int l, int r, int rt)
{
    if(l == r)
    {
        for(int i = 0; i < 3; ++i)
            if(l + i * d <= N)
                tree[rt] += num[l + i * d];
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(rt);
}
void update(int pos, int val, int l, int r, int rt)
{
    if(l < 0 || r > N || pos < 0 || pos > N)
        return;
    if(l == r)
    {
        tree[rt] += val;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)
        update(pos, val, lson);
    else
        update(pos, val, rson);
    push_up(rt);
}
ll query(int ql, int qr, int l, int r, int rt)
{
    if(ql <= l && r <= qr)
        return tree[rt];
    int mid = (l + r) >> 1;
    ll ret = -inf;
    if(ql <= mid)
        ret = max(ret, query(ql, qr, lson));
    if(qr > mid)
        ret = max(ret, query(ql, qr, rson));
    return ret;
}
 
int main()
{
    scanf("%d%d", &n, &d);
    for(int i = 1; i <= n; ++i)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        v[y].push_back(x);
        num[x]++;
    }
    build(0, N, 1);
    ll ans = 1;
    for(int i = 0; i <= N; ++i) //枚举行
    {
        ll tmp = 0;
        for(int j = 0; j < 3; ++j)
        {
            if((ll)(i + j * d) <= N)
            {
                tmp += v[i + j * d].size();
                for(auto x : v[i + j * d])
                {
                    update(x, -1, 0, N, 1);
                    update(x - d, -1, 0, N, 1);
                    update(x - 2 * d, -1, 0, N, 1);
                }
            }
        }
        tmp += query(0, N, 0, N, 1);
        ans = max(ans, tmp);
        for(int j = 0; j < 3; ++j)
        {
            if((ll)(i + j * d) <= N)
            {
                for(auto x : v[i + j * d])
                {
                    update(x, 1, 0, N, 1);
                    update(x - d, 1, 0, N, 1);
                    update(x - 2 * d, 1, 0, N, 1);
                }
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
}

 

你可能感兴趣的:(线段树,思维)