BZOJ 1107 驾驶考试EGZ POI2007

Problem

BZOJ

Solution

又是大神题。。在做dp的时候不知怎么就做到了这道题,可能跟dp唯一有点关系的可能就是lis了吧?

考虑把边反向,那么就是要1~n所有点都能到达i,那么就只需1,n满足条件即可。
不妨令f[i]表示1可以达i最少需要多少建多少条边,贪心一下,就是利用起左边的lis,然后建其他的边。
求lis可以用树状数组搞。
怎么统计答案?f是单调不降的,g是单调不升的,那么two pointer一下枚举左右端点就好了。cnt统计的是本来就满足条件的边数。

Code

#include <cstring>
#include <cstdio>
#define rg register
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=100010;
template <typename Tp> inline void getmin(Tp &x,Tp y){if(y<x) x=y;}
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{int v,w,nxt;};
struct List{
    int p,head[maxn];data edge[maxn];
    inline void insert(int u,int v)
    {
        edge[++p]=(data){v,0,head[u]};head[u]=p;
    }
}l,r;
int n,m,p,k,x,y,dir,ans,cnt,f[maxn],g[maxn],t[maxn];
inline void add(int p,int v){for(;p<=m;p+=lowbit(p)) getmax(t[p],v);}
int query(int p,int res=0){for(;p;p-=lowbit(p)) getmax(res,t[p]);return res;}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    read(n);read(m);read(p);read(k);m++;
    for(rg int i=1;i<=p;i++)
    {
        read(x);read(y);read(dir);y=m-y;
        if(dir) l.insert(x+1,y);
        else r.insert(x,y);
    }
    for(rg int i=2;i<=n;i++)
    {
        for(int j=l.head[i];j;j=l.edge[j].nxt)
          getmax(f[i],l.edge[j].w=query(l.edge[j].v)+1);
        for(int j=l.head[i];j;j=l.edge[j].nxt)
          add(l.edge[j].v,l.edge[j].w);
        f[i+1]=f[i];f[i]=i-f[i]-1;
    }
    memset(t,0,sizeof(t));
    for(rg int i=n-1;i;i--)
    {
        for(int j=r.head[i];j;j=r.edge[j].nxt)
          getmax(g[i],r.edge[j].w=query(r.edge[j].v)+1);
        for(int j=r.head[i];j;j=r.edge[j].nxt)
          add(r.edge[j].v,r.edge[j].w);
        g[i-1]=g[i];g[i]=n-i-g[i];
    }
    for(rg int i=1,j=1;i<=n;i++)
    {
        while(j<=n&&g[i]+f[j]<=k) j++;
        getmax(ans,j-i);
        if(!f[i]&&!g[i]) cnt++;
    }
    printf("%d\n",ans-cnt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/as_a_kid/article/details/81228755