LIS

112 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述
这道题我的思路是:
为了避免算重枚举需要的 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<a
(c,b+d) if c=a
(c,d) if c>a

从算法上来看,题解的思路更暴力,但代码量较少

自己的

#include<bits/stdc++.h>
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<bits/stdc++.h>
#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值