[bzoj1107]驾驶考试

转化题意,如果一个点k符合条件,当且仅当k能到达1和n
考虑如果l和r($l<r$)符合条件,容易证明那么[l,r]的所有点都将会符合条件,因此答案是一个区间
枚举答案区间[l,r],考虑如何判定答案是否合法,也就是要求满足l能到达n且r能到达1,那么预处理出f1[i]表示i能到达1的最少边数,f2[i]表示i能到达n的最少边数,相当于要满足$f1[r]+f2[l]\le k$(注意:这两个不会重复,因为边是有向的)
考虑如何预处理出f1数组(f2数组同理),其实容易发现这就是维护一个每一个位置有多个选择的最长不上升子序列,不妨将每一个位置上的数字从小到大排列,直接求lis即可
最后求答案用单调性维护即可,注意要去掉初始合法的区间长度
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 vector<int>v1[N],v2[N];
 5 int n,m,k,x,y,z,ans,a[N],f1[N],f2[N];
 6 void add(int k){
 7     x=1,y=a[0]+1;
 8     while (x<y){
 9         z=(x+y>>1);
10         if (a[z]<k)y=z;
11         else x=z+1;
12     }
13     a[0]=max(a[0],x);
14     a[x]=k;
15 }
16 int main(){
17     scanf("%d%*d%d%d",&n,&m,&k);
18     for(int i=1;i<=m;i++){
19         scanf("%d%d%d",&x,&y,&z);
20         if (z)v1[x+1].push_back(y+1);
21         else v2[x].push_back(y+1);
22     }
23     for(int i=1;i<=n;i++){
24         sort(v1[i].begin(),v1[i].end());
25         sort(v2[i].begin(),v2[i].end());
26     }
27     for(int i=2;i<=n;i++){
28         for(int j=0;j<v1[i].size();j++)
29             if ((!j)||(v1[i][j]!=v1[i][j-1]))add(v1[i][j]);
30         f1[i]=i-1-a[0];
31     }
32     memset(a,0,sizeof(a));
33     for(int i=n-1;i;i--){
34         for(int j=0;j<v2[i].size();j++)
35             if ((!j)||(v2[i][j]!=v2[i][j-1]))add(v2[i][j]);
36         f2[i]=n-i-a[0];
37     }
38     for(int i=1,j=1;(i<=n)&&(j<=n);i++){
39         while ((j<=n)&&(f2[i]+f1[j]<=k))j++;
40         ans=max(ans,j-i);
41     }
42     for(int i=1;i<=n;i++)
43         if ((!f1[i])&&(!f2[i]))ans--;
44     printf("%d",ans);
45 }
View Code

猜你喜欢

转载自www.cnblogs.com/PYWBKTDA/p/12072706.html