题目
传送门
给你一个排列,你需要把它划分成两个子序列 A、B。记
f
(
A
)
f(A)
f(A) 为 A 的 LIS 长度,
g
(
B
)
g(B)
g(B) 是 B 的 LDS 长度。问
m
a
x
(
f
(
A
)
+
g
(
B
)
)
max(f(A)+g(B))
max(f(A)+g(B))
前置知识 LIS计数
大家都知道,我们可以通过二分,
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 实现求
L
I
S
LIS
LIS 的长度。但是此时要计数怎么办呢?
我们不妨把
d
p
dp
dp 数组扩展成二维。令
d
p
i
[
]
dp_i[]
dpi[] 为:长度为
i
i
i 的
L
I
S
LIS
LIS 可能以哪些数结尾。
我们把
d
p
t
=
a
i
dp_t=a_i
dpt=ai 改成
d
p
t
.
a
p
p
e
n
d
(
a
i
)
dp_t.append(a_i)
dpt.append(ai),也就是把
a
i
a_i
ai 接在后面。
同时,我们需要维护以
a
i
a_i
ai 结尾的
L
I
S
LIS
LIS 的方案数,记为
F
i
F_i
Fi。
显然,这玩意个转移是:
F
i
=
∑
a
i
>
a
j
&
f
i
=
=
f
j
+
1
F
j
F_i=\sum_{a_i>a_j\ \& \ f_i==f_j+1} F_j
Fi=ai>aj & fi==fj+1∑Fj
那么我们就可以维护一个
s
u
m
i
,
j
sum_{i,j}
sumi,j 表示以
d
p
i
,
j
dp_{i,j}
dpi,j 为结尾的最长上升子序列方案数的前缀和。于是乎,我们同样可以通过一个二分完成转移。
int find(int x,int l,int r,vector<int> &a)
{
int ans=0;
while(l<=r)
{
int mid=l+r>>1;
if(a[mid]>x)
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
return ans;
}
void get(vector<int> a,vector<int> &f,vector<int> &F,int dec,int rev)
{
if(rev)
{
reverse(a.begin(),a.end());
}
if(dec)
{
for(int i=0; i<a.size(); i++)
a[i]=(int)a.size()-a[i]+1;
}
vector<vector<int>> pos,sum;
vector<int> dp;
f.assign(a.size(),0);
F.assign(a.size(),0);
for(int i=0; i<a.size(); i++)
{
int x=a[i];
auto t=lower_bound(dp.begin(),dp.end(),x)-dp.begin();
int c=t>0?(sum[t-1].back()-sum[t-1][find(a[i],0,pos[t-1].size()-1,pos[t-1])]):1;
c=(c%mod+mod)%mod;
if(t==dp.size())
{
dp.push_back(x);
pos.push_back({inf,x});
sum.push_back({0,c});
}
else
{
dp[t]=x;
assert(pos.size()==dp.size()&&sum.size()==dp.size());
pos[t].push_back(x);
assert(sum[t].size());
int p=sum[t].back();
sum[t].push_back((p+c)%mod);
}
f[i]=t+1;
F[i]=c;
}
if(rev)
{
reverse(f.begin(),f.end());
reverse(F.begin(),F.end());
}
}
题解
由于题目给的是一个排列,所以有一个比较显然的性质:
a
n
s
=
(
L
I
S
+
L
D
S
−
1
)
o
r
(
L
I
S
+
L
D
S
)
ans=(LIS+LDS-1)\ or\ (LIS+LDS)
ans=(LIS+LDS−1) or (LIS+LDS)
因为每个数都是不一样的,所以 LIS 与 LDS 最多存在一个交点。
现在我们需要判断是否存在一种方式选出的 LIS 和 LDS 不存在交点。
我们可以先统计出 LIS 的个数
C
1
C1
C1 和 LDS 的个数
C
2
C2
C2,那么选择的方案数就是
r
e
s
1
=
C
1
×
C
2
res1=C1\times C2
res1=C1×C2。
然后,我们再尝试统计经过
i
i
i 的 LIS 和 LDS
H
i
H_i
Hi,显然,
r
e
s
2
=
∑
H
i
res2=\sum H_i
res2=∑Hi 就是存在交点的 LIS 和 LDS 对数。
如果
r
e
s
1
=
=
r
e
s
2
res1==res2
res1==res2,那么所有 LIS 和 LDS 都存在交点,答案需要减一。
注意,这题卡自然溢出。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+7,inf=1e18,mod=1e9+7;
int find(int x,int l,int r,vector<int> &a)
{
int ans=0;
while(l<=r)
{
int mid=l+r>>1;
if(a[mid]>x)
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
return ans;
}
void get(vector<int> a,vector<int> &f,vector<int> &F,int dec,int rev)
{
if(rev)
{
reverse(a.begin(),a.end());
}
if(dec)
{
for(int i=0; i<a.size(); i++)
a[i]=(int)a.size()-a[i]+1;
}
vector<vector<int>> pos,sum;
vector<int> dp;
f.assign(a.size(),0);
F.assign(a.size(),0);
for(int i=0; i<a.size(); i++)
{
int x=a[i];
auto t=lower_bound(dp.begin(),dp.end(),x)-dp.begin();
int c=t>0?(sum[t-1].back()-sum[t-1][find(a[i],0,pos[t-1].size()-1,pos[t-1])]):1;
c=(c%mod+mod)%mod;
if(t==dp.size())
{
dp.push_back(x);
pos.push_back({inf,x});
sum.push_back({0,c});
}
else
{
dp[t]=x;
assert(pos.size()==dp.size()&&sum.size()==dp.size());
pos[t].push_back(x);
assert(sum[t].size());
int p=sum[t].back();
sum[t].push_back((p+c)%mod);
}
f[i]=t+1;
F[i]=c;
}
if(rev)
{
reverse(f.begin(),f.end());
reverse(F.begin(),F.end());
}
}
void O_o()
{
int n;
cin>>n;
vector<int> a(n);
for(int i=0; i<n; i++)
cin>>a[i];
vector<int> f,F;
get(a,f,F,0,0);
vector<int> g,G;
get(a,g,G,1,0);
vector<int> fd,Fd;
get(a,fd,Fd,0,1);
vector<int> gd,Gd;
get(a,gd,Gd,1,1);
int lis=0,lds=0;
for(int i=0; i<n; i++)
{
lis=max(lis,f[i]);
lds=max(lds,g[i]);
}
int c1=0,c2=0,res=0;
for(int i=0; i<n; i++)
{
if(f[i]==lis)
(c1+=F[i])%=mod;
if(g[i]==lds)
(c2+=G[i])%=mod;
}
for(int i=0; i<n; i++)
{
int ass=1;
if(f[i]+gd[i]==lis+1)
(ass*=F[i]*Gd[i]%mod)%=mod;
else
ass=0;
if(g[i]+fd[i]==lds+1)
(ass*=G[i]*Fd[i]%mod)%=mod;
else
ass=0;
(res+=ass)%=mod;
}
int ans=lis+lds;
if(res==c1*c2%mod)
ans--;
cout<<ans<<"\n";
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cout<<fixed<<setprecision(12);
int T=1;
cin>>T;
while(T--)
{
O_o();
}
}