【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 }