题目链接

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

思路

我们可以发现几个结论:

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

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

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

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

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

  1. 找出原图中能走到所有点的路;
  2. 找出最左边和最右边到所有点的最长不上升子序列的长度;
  3. 找出添加边数<=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;
}