[正睿集训2021] LIS
一、题目
二、解法
设 \(dp[i]\) 为到 \(i\) 的最大长度,那么当 \(a_j\times x_i+b_j\geq y_i\) 可以转移:
\[dp[i]=dp[j]+1
\]
这个题没有掩饰啊,直接就把一次函数甩给你了,摆明了就是要让你用李超树。具体来说转移 \(i\) 的时候有 \([1,i)\) 这些线段,\(x_i\leq300000\) 说明它可以作为李超树的下标。
但是李超树不是只能解决一次函数的最值吗?怎么又和不等式扯上关系的?我们可以二分答案 \(x\) ,那么对于 \(dp\) 值在 \([x,n]\) 的线段都是可以用的,我们求出他在 \(x_i\) 的最值是否大于 \(y\) 即可。
但是要保留 \([x,n]\) 的线段并不是很容易,我们可以使用树套树来解决这个问题,也就是外层线段树,内层李超树,李超树要动态开点,那么我们在线段树上二分就行了,由于李超树是全局线段所以复杂度是一个 \(\log\)
那么时间复杂度 \(O(n\log^2 n)\),空间复杂度 \(O(n\log n)\) ,看起来很好写是吧。
但是我的答案输出成了 \(dp[n]\) ,实际上应该是 \(\max_{i=1}^n dp[i]\),老子调了好久,\(\tt cnm\) 的 \(\tt sb\) 题。
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 150005;
const int N = 50*M;
const int up = 300000;
const int inf = 0x3f3f3f3f;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,cnt,ans,a[M],b[M],x[M],y[M],dp[M],rt[4*M],ls[N],rs[N];
struct node
{
int k,b;
node(int K=0,int B=-inf) : k(K) , b(B) {}
void clear() {k=0;b=-inf;}
int ask(int x)
{
return x*k+b;
}
}tr[N];
void upd(int &i,int l,int r,node x)
{
if(!i)
{
i=++cnt;
ls[i]=rs[i]=0;tr[i].clear();
}
int mid=(l+r)>>1;
if(x.ask(mid)>tr[i].ask(mid))
{
if(x.ask(l)<tr[i].ask(l)) upd(ls[i],l,mid,tr[i]);
if(x.ask(r)<tr[i].ask(r)) upd(rs[i],mid+1,r,tr[i]);
tr[i]=x;
}
if(x.ask(mid)<tr[i].ask(mid))
{
if(x.ask(l)>tr[i].ask(l)) upd(ls[i],l,mid,x);
if(x.ask(r)>tr[i].ask(r)) upd(rs[i],mid+1,r,x);
}
}
int ask(int i,int l,int r,int x)
{
if(!i) return -inf;
int mid=(l+r)>>1,res=tr[i].ask(x);
if(l==r) return res;
if(mid>=x) return max(ask(ls[i],l,mid,x),res);
return max(ask(rs[i],mid+1,r,x),res);
}
void ins(int i,int l,int r,int id,node x)
{
upd(rt[i],0,up,x);
if(l==r) return ;
int mid=(l+r)>>1;
if(mid>=id) ins(i<<1,l,mid,id,x);
else ins(i<<1|1,mid+1,r,id,x);
}
int solve(int i,int l,int r,int x,int y)
{
if(ask(rt[i],0,up,x)<y)
return 0;
if(l==r) return l;
int mid=(l+r)>>1;
if(ask(rt[i<<1|1],0,up,x)>=y)
return solve(i<<1|1,mid+1,r,x,y);
if(ask(rt[i<<1],0,up,x)>=y)
return solve(i<<1,l,mid,x,y);
}
signed main()
{
T=read();
while(T--)
{
n=read();cnt=ans=0;//nmsl
for(int i=1;i<=4*n;i++) rt[i]=0;
for(int i=1;i<=n;i++)
{
a[i]=read();b[i]=read();
x[i]=read();y[i]=read();
}
for(int i=1;i<=n;i++)
{
dp[i]=solve(1,1,n,x[i],y[i])+1;
ins(1,1,n,dp[i],node(a[i],b[i]));
}
for(int i=1;i<=n;i++)
ans=max(ans,dp[i]);
printf("%lld\n",ans);
}
}