题意:
在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;
}