如题。$N \leqslant 5000$。
感觉自己思路永远都是弯弯绕绕的。。即使会做也会被做繁掉。。果然还是我太菜了。
递减不爽,先倒序输入算了。第一问做个LIS没什么说的。第二问统计个数,考虑什么时候是重复计算的。$g[i]$表示第$i$个数结尾的LIS长度的方案(不重复)。当统计时dp到一个数时显然从前面满足$f_j+1=f_i且A_j<A_i$条件的累加过来,$A_j$不同的时候,自然不会有重复;当有相同的数且f一样时,如果这几种都加入,就重复了。而相同的几个数字显然靠后的方案统计到的更多,所以每次只取最靠右的那个数累加上去即可。实现上,开一个桶,记录vis,结束后再吐出来。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #define dbg(x) cerr<<#x<<" = "<<x<<endl 8 #define _dbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl 9 using namespace std; 10 typedef long long ll; 11 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;} 12 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;} 13 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 14 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 15 template<typename T>inline T read(T&x){ 16 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 17 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 18 } 19 const int N=5000+7,M=1<<17,INF=0x3f3f3f3f; 20 int f[N],g[N],lis[N],a[N],vis[M],bin[N]; 21 int n,len,ans,tot,cnt; 22 23 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout); 24 read(n);for(register int i=1;i<=n;++i)read(a[n-i+1]); 25 lis[len=1]=INF; 26 for(register int i=1;i<=n;++i){ 27 f[i]=lower_bound(lis+1,lis+len+1,a[i])-lis; 28 if(f[i]>len)lis[++len]=a[i];else lis[f[i]]=a[i]; 29 MAX(ans,f[i]); 30 } 31 for(register int i=1;i<=n;++i){ 32 if(f[i]==1){g[i]=1;continue;} 33 for(register int j=i-1;j;--j)if(!vis[a[j]]&&a[j]<a[i]&&f[j]+1==f[i])vis[bin[++tot]=a[j]]=1,g[i]+=g[j]; 34 while(tot)vis[bin[tot--]]=0; 35 } 36 for(register int i=n;i;--i) if(!vis[a[i]]&&f[i]==ans)cnt+=g[i],vis[a[i]]=1; 37 printf("%d %d\n",ans,cnt); 38 return 0; 39 }
嗯这个是本人极其繁琐的想法。发现自己傻了有没有。。而且数据大的话桶不就挂了吗。。所以依据原来思路,改变对于dp状态的定义。$g[i]$表示第$i$个数结尾的长度为LIS的方案数,且不包括之前所有和自己相同且$len_{LIS}$相同的数的方案。这样每次转移时遇到相同即break。保证取的决策一定来源于上一个相同数和现在这个数之间。具体还是看code吧。。
另外还有一种做法是网络流。。不想写了QwQ。 ←不行,建边都会建炸掉
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #define dbg(x) cerr<<#x<<" = "<<x<<endl 8 #define _dbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl 9 using namespace std; 10 typedef long long ll; 11 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;} 12 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;} 13 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 14 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 15 template<typename T>inline T read(T&x){ 16 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 17 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 18 } 19 const int N=5000+7,M=1<<17,INF=0x3f3f3f3f; 20 int f[N],g[N],lis[N],a[N]; 21 int n,len,ans,tot,cnt; 22 23 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout); 24 read(n);for(register int i=1;i<=n;++i)read(a[n-i+1]); 25 lis[len=1]=INF; 26 for(register int i=1;i<=n;++i){ 27 f[i]=lower_bound(lis+1,lis+len+1,a[i])-lis; 28 if(f[i]>len)lis[++len]=a[i];else lis[f[i]]=a[i]; 29 MAX(ans,f[i]); 30 } 31 g[0]=1; 32 for(register int i=1;i<=n;++i){ 33 for(register int j=i-1;~j;--j){ 34 if(a[i]==a[j]&&f[i]==f[j])break; 35 if(f[i]==f[j]+1&&a[j]<a[i])g[i]+=g[j]; 36 } 37 } 38 for(register int i=1;i<=n;++i)if(f[i]==ans)cnt+=g[i]; 39 printf("%d %d\n",ans,cnt); 40 return 0; 41 }