题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=1107
思路
我们可以发现几个结论:
- 一条自南向北的路能到其他所有自南向北的路,其充要条件是 这条路能到最左边和最右边的路。
- 一条自南向北的路能到另外一条路,充要条件是 他们之间相连的东西走向的路能组成一个不下降序列。
- 将所有路反向后,原图中A路能到B路,当且仅当 反向后B路能到A路。
- 从一条自南向北的路出发后,能到达的自南向北的道路一定是连续的区间。
由1,3可得,反向后的图中由最左和最右能走到的点的交集,就是原图中能走到所有点的路。
由4可得,上面所说的路是一个区间。
有2,3可得,最左的路能走到点,需要添加的边数,就是,其中是的最长不上升子序列。最右也如此。
那么我们就可以写出算法流程了:
- 找出原图中能走到所有点的路;
- 找出最左边和最右边到所有点的最长不上升子序列的长度;
- 找出添加边数的一段区间,使这段区间能够都与最左和最右联通,并更新答案。
代码
#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;
}