BZOJ1107 : [POI2007]驾驶考试egz

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

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

把图上下翻转后,从左往右依次计算fl[i],有fl[i]=i-1-左边LIS的长度,用树状数组维护即可$O(n\log 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;

}

  

你可能感兴趣的:(2007)