bzoj1107[POI2007]驾驶考试egz LIS

73 篇文章 0 订阅
21 篇文章 0 订阅

又见到神题了。。。
题解做法超神了。。
首先我们可以有一个并不显然的想法,就是把所有的边反向以后,题目要求的条件就变成了从任意一个点到i可到达,同时化简一下可以发现只要最左边和最右边可以到达就好了。
设l[i]表示1-i至少需要加多少条边,r[i]表示n到i需要加多少条边。
可以发现l[i]=i-1-pre,pre为左边LIS的长度,用树状数组维护即可O(nlogn)求出。
r[i]同理。
然后就是求l[i]+r[j]<=k的i,j,由于l递增r递减,所以用一个双指针维护一下就好了。
好强啊%%%Claris1.2Krank2,不知道高到哪里去了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define judge(x,y) (x>=lx&&x<=rx&&y>=ly&&y<=ry) 
using namespace std;
const int N=1e6+5;
const int inf=0x7fffffff;
struct node
{
    int v,f;
    node*next;
}*gl[N],*gr[N],pool[N],*tot=pool,*e;
int n,m,p,k,bit[N];
int l[N],r[N];
int pre,ans,cnt;
inline void addl(int x,int y)
{
    e=tot++;
    e->v=y;
    e->next=gl[x];
    gl[x]=e;
}
inline void addr(int x,int y)
{
    e=tot++;
    e->v=y;
    e->next=gr[x];
    gr[x]=e;
}
inline void up(int &a,int b)
{
    if (a<b)a=b;
}
inline void add(int x,int y)
{
    while (x<=m)
    {
        up(bit[x],y);
        x+=x&-x;
    }
}
inline int ask(int x)
{
    int ans=0;
    while (x)
    {
        up(ans,bit[x]);
        x-=x&-x;
    }
    return ans;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&k);
    m++;
    while (p--)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        y=m-y;
        if (z)addl(x+1,y);
        else addr(x,y);
    }
    fo(i,2,n)
    {
        for(e=gl[i];e;e=e->next)
        up(pre,e->f=ask(e->v)+1);
        for(e=gl[i];e;e=e->next)
        add(e->v,e->f);
        l[i]=i-1-pre;
    }
    int i;
    for(pre=0,i=1;i<=m;i++)bit[i]=0;
    fd(i,n-1,1)
    {
        for(e=gr[i];e;e=e->next)
        up(pre,e->f=ask(e->v)+1);
        for(e=gr[i];e;e=e->next)
        add(e->v,e->f);
        r[i]=n-i-pre;
    }
    int j=1;
    fo(i,1,n)
    {
        while (j<=n&&r[i]+l[j]<=k)j++;
        up(ans,j-i);
        if (!l[i]&&!r[i])cnt++;
    }
    return printf("%d",ans-cnt),0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值