解题:POI 2007 Driving Exam

题面

有点意思的题

从一个位置$i$出发可以到达每一个位置即是从$1,n$出发可以到达$i$。然后有了一个做法:把图上下反转后建反图,这样就可以求从一个点$i$到达左右两侧的花费$dp[i][0/1]$了,这个花费就是当前总长度-到这个点为止的LIS长度(左右各求一遍)。因为还要考虑边的这个问题,可以用一个权值树状数组维护前/后缀最大值来实现。可以发现合法点的左侧都能到达左端,右侧都能到达右端,所以其实我们找的是一段区间,即找一段区间$(l,r)$使得$dp[l][1]+dp[r][0]<=k$,发现$dp$数组两维都是单调的,直接双指针即可。

 1 #include<cstdio>
 2 #include<vector>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N=100005;
 7 struct a{int h,v;}; vector<a> m1[N],m2[N]; 
 8 int n,m,d,k,t1,t2,typ,len,ans,p1,p2;
 9 int tr[N],dp[N][2];
10 void maxx(int pos,int num)
11 {
12     while(pos<=m)
13         tr[pos]=max(tr[pos],num),pos+=pos&-pos;
14 }
15 int query(int pos)
16 {
17     int ret=0;
18     while(pos)
19         ret=max(ret,tr[pos]),pos-=pos&-pos;
20     return ret;
21 }
22 int main ()
23 {
24     scanf("%d%d%d%d",&n,&m,&d,&k),m++;
25     for(int i=1;i<=d;i++)
26     {
27         scanf("%d%d%d",&t1,&t2,&typ);
28         if(typ) m1[t1+1].push_back((a){m-t2,0});
29         else m2[t1].push_back((a){m-t2,0});
30     }
31     for(int i=1;i<=n;i++)
32     {
33         int siz=m1[i].size();
34         for(int j=0;j<siz;j++)
35         {
36             m1[i][j].v=query(m1[i][j].h)+1;
37             len=max(len,m1[i][j].v);
38         }
39         dp[i][0]=i-len-1;
40         for(int j=0;j<siz;j++)
41             maxx(m1[i][j].h,m1[i][j].v);
42     }
43     len=0,memset(tr,0,sizeof tr);
44     for(int i=n;i;i--)
45     {
46         int siz=m2[i].size();
47         for(int j=0;j<siz;j++)
48         {
49             m2[i][j].v=query(m2[i][j].h)+1;
50             len=max(len,m2[i][j].v);
51         }
52         dp[i][1]=n-len-i;
53         for(int j=0;j<siz;j++)
54             maxx(m2[i][j].h,m2[i][j].v);
55     }
56     len=0,p1=p2=1;
57     while(p1<=n)
58     {
59         while(p2<=n&&dp[p1][1]+dp[p2][0]<=k) p2++;
60         ans=max(ans,p2-p1); if(!dp[p1][0]&&!dp[p1][1]) len++; p1++;
61     }
62     printf("%d",ans-len);
63     return 0;
64 }
View Code

猜你喜欢

转载自www.cnblogs.com/ydnhaha/p/9756228.html