这道题我的思路是:
为了避免算重枚举需要的 LIS (最强不下降子序列),就是 A[i…n] 强制包含 i 的 LIS 和强制包含 i 的 LDS 。其余元素怎么放都无所谓(方案数*2),再和 LIS 的方案数和 LDS 的方案数乘起来就行了。
题解的思路是:
直接求LIS/LDS 的方案数:f[i]是一个二元组,(以i为结尾的最长 LIS 长度,最长LIS的方案数) ,用 (a,b) 去更新 (c,d) 的时候就是:
(a,b) if c (c,b+d) if c=a
(c,d) if c>a
从算法上来看,题解的思路更暴力,但代码量较少
自己的
#include
using namespace std;
typedef pair<int, int> par;
const int maxn=500010,mod=1000000007;
inline int add(int a,int b)
{
int ret=a+b;
if(ret>=mod)
ret-=mod;
return ret;
}
inline int mul(int a,int b)
{
long long int res=(long long int)a*b;
if(res>=mod)
res%=mod;
return res;
}
int n;
int niz[maxn],dva[maxn];
par A[maxn],B[maxn];
par gore[maxn],dolje[maxn];
par rj;
par spoji(par a,par b)
{
if(b.first>a.first)
{
a.first=b.first;
a.second=b.second;
}
else if(b.first==a.first)
a.second=add(a.second,b.second);
return a;
}
void ubaci_gore(int x,par v)
{
x+=5;
for(;x<maxn;x+=x&-x)
gore[x]=spoji(gore[x],v);
}
par upit_gore(int x)
{
x+=5;
par res(0,0);
for(;x>0;x-=x&-x)
res=spoji(res,gore[x]);
return res;
}
void ubaci_dolje(int x,par v)
{
x+=5;
for(;x>0;x-=x&-x)
dolje[x]=spoji(dolje[x],v);
}
par upit_dolje(int x)
{
x+=5;
par res(0,0);
for(;x<maxn;x+=x&-x)
res=spoji(res,dolje[x]);
return res;
}
void sazmi()
{
vector<int>v;
for(int i=0;i<n;i++)
v.push_back(niz[i]);
sort(v.begin(),v.end());
v.resize(unique(v.begin(),v.end())-v.begin());
for(int i=0;i<n;i++)
niz[i]=lower_bound(v.begin(),v.end(),niz[i])-v.begin();
}
void sredi_gore()
{
for(int i=n-1;i>=0;i--)
{
par p=upit_gore(niz[i]-1);
if(p.first==0)
{
A[i]=par(0,1);
ubaci_gore(niz[i],par(1,1));
}
else
{
A[i]=p;
p.first++;
ubaci_gore(niz[i],p);
}
}
}
void sredi_dolje()
{
for(int i=n-1;i>=0;i--)
{
par p=upit_dolje(niz[i]+1);
if(p.first==0)
{
B[i]=par(0,1);
ubaci_dolje(niz[i],par(1,1));
}
else
{
B[i]=p;
p.first++;
ubaci_dolje(niz[i],p);
}
}
}
void postavi()
{
dva[0]=1;
for(int i=1;i<maxn;i++)
dva[i]=mul(dva[i-1],2);
}
void glavno()
{
for(int i=0;i<n;i++)
rj=spoji(rj,par(A[i].first+1+B[i].first,mul(A[i].second,B[i].second)));
rj.second=mul(rj.second,dva[n-rj.first]);
}
int main()
{
postavi();
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&niz[i]);
sazmi();
sredi_gore();
sredi_dolje();
glavno();
printf("%d %d\n",rj.first,rj.second);
return 0;
}
题解的
#include
#define LL long long
#define ULL unsigned long long
#define DD double
#define maxn 200005
using namespace std;
inline int read()
{
int red=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
while (ch>='0'&&ch<='9') red=(red<<3)+(red<<1)+ch-'0',ch=getchar();
return red*f;
}
const int TT=(1e9)+7;
struct CY
{
int len;
LL cnt;
CY operator +(const CY b)const
{
if(len<b.len)
return b;
if(len==b.len)
return (CY){len,(cnt+b.cnt)%TT};
if(len>b.len)
return (CY){len,cnt};
}
}tre[maxn],LIS[maxn],LDS[maxn],Ans;
LL fac[maxn];
int n,A[maxn];
int B[maxn],end;
void Add1(int x,CY data)
{
for(;x<=n;x+=x&-x)
tre[x]=tre[x]+data;
}
CY Get1(int x)
{
CY S=(CY){0,0};
for(;x;x-=x&-x)
S=S+tre[x];
return S;
}
void Add2(int x,CY data)
{
for(;x;x-=x&-x)
tre[x]=tre[x]+data;
}
CY Get2(int x)
{
CY S=(CY){0,0};
for(;x<=n;x+=x&-x)
S=S+tre[x];
return S;
}
void pre()
{
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=(fac[i-1]*2)%TT;
}
int main()
{
n=read();
pre();
for(int i=1;i<=n;i++)
B[i]=A[i]=read();
sort(B+1,B+n+1);
end=unique(B+1,B+n+1)-B-1;
for(int i=1;i<=n;i++)
A[i]=lower_bound(B+1,B+end+1,A[i])-B;
for(int i=n;i;i--)
{
CY x=Get1(A[i]-1);
if(!x.cnt)
++x.cnt;
LIS[i]=x;
++x.len;
Add1(A[i],x);
}
memset(tre,0,sizeof tre);
for(int i=n;i;i--)
{
CY x=Get2(A[i]+1);
if(!x.cnt)
++x.cnt;
LDS[i]=x;
++x.len;
Add2(A[i],x);
}
for(int i=1;i<=n;i++)
Ans=Ans+(CY){LIS[i].len+LDS[i].len+1,LIS[i].cnt*LDS[i].cnt%TT};
printf("%d %lld\n",Ans.len,(Ans.cnt*fac[n-Ans.len])%TT);
return 0;
}