又见到神题了。。。
题解做法超神了。。
首先我们可以有一个并不显然的想法,就是把所有的边反向以后,题目要求的条件就变成了从任意一个点到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;
}