Codeforces 527D Clique Problem (dp+树状数组+离散化)

题意:

在x坐标轴上有n个不同的点,每个点位置为xi,点权为wi。若|xi - xj| ≥ wi + wj.该式子,则i和j点可以连一条边。现在让你找出一个最大的点集,使得集合内任意两点有一条边连着。

输出最大点集内点的数量。


思路:

感谢杰哥提供的思路。


dp:

现有3个点,x1 < x2 < x3。d1为x1~x2的距离,d2为x2~x3的距离。

若x1和x2有边相连,x2和x3有边相连。即

d1 >= w1+w2;

d2 >= w2+w3;

则有 d1+d2 > w1+w3;

这个式子说明了如果点2和点3相连,则点3可以和点2相连的所有点都相连。


于是我们可以定义这样的dp[i]:以点i为终点的最大点数。

dp[i] = max(dp[i], dp[j]+1);(前提i,j满足上面的式子并且xi > xj)

直接两个for循环可以求出。复杂度O(n^2);


然后就是优化了。(树状数组+离散化)

|xi - xj| ≥ wi + wj. 如果我们保证xi>xj,则有

xi-wi >= xj+wj。

那么我们就对每个xi+wi进行离散化,采用树状数组保存最大值。

每次加入一个新点i,就询问<=xi-wi的范围的最大值,然后+1,再插入到xi+wi所对应的位置。


code:

#include <bits/stdc++.h>
using namespace std;

const int N = 2*1e5+5;
typedef long long LL;
typedef pair <int, int> pa;
struct dct {
    int a[N], n;
    dct():n(0){}
    void push(int val) {
        a[n++] = val;
    }
    void ok() {
        sort(a, a+n);
        n = unique(a, a+n) - a;
    }
    inline int find(int val) {
        return upper_bound(a, a+n, val)-a+1;
    }
}lsh;

int n;
pa p[N];
int bit[N];

inline int lowbit(int x) {
    return x&(-x);
}
int query(int idx) {
    int ret = 0;
    while(idx > 0) {
        ret = max(ret, bit[idx]);
        idx -= lowbit(idx);
    }
    return ret;
}

void update(int idx, int val) {
    while(idx <= n) {
        bit[idx] = max(bit[idx], val);
        idx += lowbit(idx);
    }
}

int solve() {
    sort(p+1, p+n+1);
    int ret = 0;
    for(int i = 1;i <= n; i++) {
        int tmp = p[i].first-p[i].second;
        int idx = lsh.find(tmp)-1;
        ret = max(ret, query(idx)+1);
        tmp = p[i].first+p[i].second;
        update(lsh.find(tmp)-1, ret);
    }
    return ret;
}
    
        
int main() {
    scanf("%d", &n);
    int x, y;
    for(int i = 1;i <= n; i++) {
        scanf("%d%d", &x, &y);
        lsh.push(x+y);
        p[i] = make_pair(x, y);
    }
    lsh.ok();
    printf("%d\n", solve());
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值