【dp】CF76F. Tourist

http://codeforces.com/problemset/problem/76/F

       一个很奇怪的问题,某个人在X坐标上左右移动,方向任意随时可以改变,也随时可以停下,其最大速度为V。在坐标上某些点某些时刻(xi,ti)有特别的事情发生,如果那一瞬间停留在那个点上就将经历可以神奇的事情,问在这个人0时刻可以选择任意位置,以及0时刻在x=0位置上分别可以经历最多特别的事情是多少。。

      作为一个暴力的acmer,我只想到用树套树n(logn)^2 近乎模拟的解决这个问题。。后来看了题解发现问题可以非常神奇的转移成简单的单调队列问题,证明如下:

       设Pi = xi+ti*V  Qi = xj+tj*V

       若ti <= tj  且 |xi-xj| <= |ti-tj|*V ,则此人可以经历过i事件再去经历j事件。

        当xi>xj 有 xi + ti*V <= xj+tj*V 同时必然有 xi+ti*V <= xj+tjV, 同理当xi<xj 这两条不等式也成立,即若此人可以经历i事件以及j事件,且ti<=tj,则有Pi<=Pj && Qi<=Qj

       最后我们再来去掉ti<=tj这个条件,简单设ti>tj  && Pi<=Pj&& Qi<=Qj 并化简出矛盾即可以。。。

      到此我们成功将问题转化为求最长序列满足,pi , qi 均单调递增,先按pi排序,之后根据qi求最长不下降子序列即可以。详见代码

View Code
 1 //By Lin
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 100050
 6 #define X first
 7 #define Y second
 8 using namespace std;
 9 typedef long long LL;
10 
11 int    n;
12 int    x[maxn],t[maxn],V;
13 pair<LL,LL> data[maxn];
14 
15 int        solve(){
16     LL     que[maxn];
17     int    cnt = 0; 
18     sort( data , data+n );
19     for (int i = 0; i<n; i++ ){
20         int l = 0 , r = cnt-1, k = -1; 
21         while ( l <= r ){
22             int mid = (l+r)>>1;
23             if ( que[mid] <= data[i].Y ) k = mid ,l = mid+1;
24             else r = mid-1;
25         }
26         que[k+1] = data[i].Y;
27         if ( k+1 == cnt ) cnt++;
28     }
29     return cnt;
30 }
31 int        main(){
32     scanf("%d", &n );
33     for (int i = 0; i<n; i++) scanf("%d%d", &x[i], &t[i] );
34     scanf("%d", &V );
35     for (int i = 0; i<n; i++) 
36         data[i].X = ((LL)V)*t[i] - x[i],
37         data[i].Y = ((LL)V)*t[i] + x[i];
38     int a,b;
39     b = solve();
40     int cnt = 0;
41     for (int i = 0; i<n; i++)
42         if ( abs(x[i]) <= ((LL)V)*t[i] ) 
43             data[cnt].X = ((LL)V)*t[i] - x[i],
44             data[cnt++].Y = ((LL)V)*t[i] + x[i];
45     n = cnt;
46     a = solve();
47     printf("%d %d\n" , a, b );
48 }

 

posted @ 2012-12-15 16:25  lzqxh  阅读(343)  评论(0编辑  收藏  举报