BZOJ 1107 [POI2007]驾驶考试egz

本文探讨了一种路径可达性的优化问题,通过分析特定条件下道路之间的连接规律,提出了一种有效的算法来解决如何通过最少的边添加使得路径可达的问题。文章详细介绍了算法的设计思路和实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=1107

思路

我们可以发现几个结论:

  1. 一条自南向北的路能到其他所有自南向北的路,其充要条件是 这条路能到最左边和最右边的路。
  2. 一条自南向北的路能到另外一条路,充要条件是 他们之间相连的东西走向的路能组成一个不下降序列。
  3. 将所有路反向后,原图中A路能到B路,当且仅当 反向后B路能到A路。
  4. 从一条自南向北的路出发后,能到达的自南向北的道路一定是连续的区间。

由1,3可得,反向后的图中由最左和最右能走到的点的交集,就是原图中能走到所有点的路。

由4可得,上面所说的路是一个区间。

有2,3可得,最左的路能走到 i i 点,需要添加的边数,就是iL,其中 L L [1,i]的最长不上升子序列。最右也如此。

那么我们就可以写出算法流程了:

  1. 找出原图中能走到所有点的路;
  2. 找出最左边和最右边到所有点的最长不上升子序列的长度;
  3. 找出添加边数 <=p <= p 的一段区间,使这段区间能够都与最左和最右联通,并更新答案。

代码

#include <cstdio>
#include <vector>
#include <algorithm>

const int maxn=100000;
const int inf=0x3f3f3f3f;

typedef std::vector<int> vi;
typedef std::vector<int>::iterator viit;

int n,m,p,k,l[maxn+10],r[maxn+10],nowans,f[maxn+10];
vi gl[maxn+10],gr[maxn+10];

int main()
{
  scanf("%d%d%d%d",&n,&m,&k,&p);
  while(k--)
    {
      int a,b,c;
      scanf("%d%d%d",&a,&b,&c);
      if(c)
        {
          gl[a+1].push_back(b);
        }
      else
        {
          gr[a].push_back(b);
        }
    }
  gl[1].push_back(inf);
  gr[n].push_back(inf);
  for(register int i=1; i<=n; ++i)
    {
      std::sort(gl[i].begin(),gl[i].end());
      std::sort(gr[i].begin(),gr[i].end());
    }
  for(register int i=1; i<=n; ++i)
    {
      for(viit it=gl[i].begin(); it!=gl[i].end(); ++it)
        {
          if(!nowans)
            {
              f[++nowans]=-(*it);
            }
          else
            {
              int now=std::upper_bound(f+1,f+nowans+1,-(*it))-f;
              f[now]=-(*it);
              if(now>nowans)
                {
                  nowans=now;
                }
            }
        }
      l[i]=i-nowans;
    }
  nowans=0;
  for(register int i=n; i; --i)
    {
      for(viit it=gr[i].begin(); it!=gr[i].end(); ++it)
        {
          if(!nowans)
            {
              f[++nowans]=-(*it);
            }
          else
            {
              int now=std::upper_bound(f+1,f+nowans+1,-(*it))-f;
              f[now]=-(*it);
              if(now>nowans)
                {
                  nowans=now;
                }
            }
        }
      r[i]=n-i+1-nowans;
    }
  int now=1,ans=0,cnt=0;
  for(register int i=1; i<=n; ++i)
    {
      if(l[i]+r[i]==0)
        {
          ++cnt;
        }
      while((now<=n)&&(l[i]+r[now]>p))
        {
          ++now;
        }
      ans=std::max(ans,i-now+1);
    }
  printf("%d\n",ans-cnt);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值