codeforces 527D D. Clique Problem(二分+線段樹+貪心+dp)
阿新 • • 發佈:2019-01-25
題目連結:
題目大意:
給出一些點的
題目分析:
- 首先我們想要知道哪些點之間是存在邊的,
|xi−xj|≥wi+wj 的絕對值符號去掉不影響邊,因為不等式右邊是加法,所以
xi−xj≥wi+wj⇒xi−wi≥wj+xj
所以我們可以先按照wj+xj 排序,那麼xi,那麼對於每個xi ,我們只需要找到不大於xi−wi 的j中的dp值最大的那個轉移即可。區間最大通過線段樹維護。利用二分找到比xi−wi 小的最大的j,定為k
我們得到:
dp[i]=maxj=0kdp[j]
那麼就是隻考慮前面的這i個點得到的最大的點集的數量。
AC程式碼:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define INF (1<<29)
#define MAX 200007
using namespace std;
int n;
struct Node
{
int w,x;
bool operator < ( const Node& a ) const
{
return w+x < a.w+a.x;
}
}p[MAX];
int bsearch ( int i )
{
int l = 0 , r = i-1 ,mid;
while ( l != r )
{
mid = (l+r+1)>>1;
if ( p[i].x - p[i].w < p[mid].x + p[mid].w ) r = mid-1;
else l = mid;
}
return l;
}
struct Tree
{
int l,r,maxn;
}tree[MAX<<2 ];
void build ( int u , int l , int r )
{
tree[u].l = l;
tree[u].r = r;
tree[u].maxn = 0;
if ( l == r ) return;
int mid = l+r>>1;
build ( u<<1 , l , mid );
build ( u<<1|1 , mid+1 , r );
}
void push_up ( int u )
{
tree[u].maxn = max ( tree[u<<1].maxn , tree[u<<1|1].maxn );
}
void update ( int u , int x , int v )
{
int l = tree[u].l;
int r = tree[u].r;
if ( l == r )
{
tree[u].maxn = v;
return;
}
int mid = l+r>>1;
if ( x > mid ) update ( u<<1|1 , x , v );
else update ( u<<1 , x , v );
push_up ( u );
}
int query ( int u , int left , int right )
{
int l = tree[u].l , r = tree[u].r;
if ( left <= l && r <= right )
return tree[u].maxn;
int mid = l+r>>1;
int ret = 0;
if ( l <= mid && r >= left ) ret = query ( u<<1 , left , right );
if ( left <= r && right > mid )
ret = max ( ret ,query ( u<<1|1 , left , right ) );
return ret;
}
int main ()
{
while ( ~scanf ( "%d" , &n ))
{
for ( int i = 1 ; i <= n ; i++ )
scanf ( "%d%d" , &p[i].x , &p[i].w );
sort ( p+1 , p+n+1 );
build ( 1 , 0 , n );
update ( 1 , 0 , 0 );
p[0].x = - INF;
p[0].w = - INF;
int ans = 0;
for ( int i = 1 ; i <= n ; i++ )
{
int x = bsearch ( i );
//dp[i] = dp[x]+1;
int v = query ( 1 , 0 , x );
update ( 1 , i , v+1 );
ans = max ( v+1 , ans );
}
printf ( "%d\n" , ans );
}
}