有点意思的题
从一个位置$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 }