BZOJ1107 : [POI2007]驾驶考试egz

i可以作为起点说明把边反向后可以从1和n到达i。

设fl[i]表示从1到达i至少需要加几条边,fr[i]表示从n到达i至少需要加几条边。

把图上下翻转后,从左往右依次计算fl[i],有fl[i]=i-1-左边LIS的长度,用树状数组维护即可$O(nlog n)$求出。

从右往左计算fr[i]同理。

然后需要求i,j(i<=j),使得fr[i]+fl[j]<=k。

由于fl单调递增,fr单调递减,因此随着i不断右移,j也会不断右移,所以可以$O(n)$求出。

#include<cstdio>
#define N 100010
int n,m,p,k,i,j,x,y,z,bit[N],fl[N],fr[N],pre,ans,cnt;
struct E{int v,f;E*nxt;}*gl[N],*gr[N],pool[N],*cur=pool,*e;
inline void addl(int x,int y){e=cur++;e->v=y;e->nxt=gl[x];gl[x]=e;}
inline void addr(int x,int y){e=cur++;e->v=y;e->nxt=gr[x];gr[x]=e;}
inline void up(int&a,int b){if(a<b)a=b;}
inline void add(int x,int y){for(;x<=m;x+=x&-x)up(bit[x],y);}
inline int ask(int x){int t=0;for(;x;x-=x&-x)up(t,bit[x]);return t;}
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
int main(){
  read(n),read(m),read(p),read(k);m++;
  while(p--){
    read(x),read(y),read(z);y=m-y;
    z?addl(x+1,y):addr(x,y);
  }
  for(i=2;i<=n;i++){
    for(e=gl[i];e;e=e->nxt)up(pre,e->f=ask(e->v)+1);
    for(e=gl[i];e;e=e->nxt)add(e->v,e->f);
    fl[i]=i-1-pre;
  }
  for(pre=0,i=1;i<=m;i++)bit[i]=0;
  for(i=n-1;i;i--){
    for(e=gr[i];e;e=e->nxt)up(pre,e->f=ask(e->v)+1);
    for(e=gr[i];e;e=e->nxt)add(e->v,e->f);
    fr[i]=n-i-pre;
  }
  for(i=j=1;i<=n;i++){
    while(j<=n&&fr[i]+fl[j]<=k)j++;
    up(ans,j-i);
    if(!fl[i]&&!fr[i])cnt++;
  }
  return printf("%d",ans-cnt),0;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/4616123.html