这里是题面
–这是一道计算不重复的最长下降子序列的方案数的题
–首先很容易想到,先计算最长下降子序列,然后遍历一遍f数组,如果f[i]==LDS,ans++
–但这样一定有重复的计算
–去重1:
记g[i]为以第i个数结尾的最长下降子序列的方案数,那么g[i]一定是由点g[j]转移而来,g[j]满足:j < i ,a[j] > a[i],f[j]==f[i]-1。但是,如果在a[i]前有k个数字和a[j]相等,那么只有最后一个和它相等的数(有可能是他自己)转移过来的才是答案,其他的都是重复的计算,所以我们可以用bool数组,倒序循环,如果一个数被纪录过答案,直接跳过,那么最后的答案就是所有f[i]==LDS的g[i]的和
但是用bool数组,数组开不了这么大,codevs又显示我TLE,那就用map吧
–去重2:
但是,如果有这种情况:
a:3 2 2
g:1 1 1
这样,答案就会被记为2,显然答案应该是1
也就是说,我们忽略了a[i]本身和a[j]相等的情况,没有去重
我们可以在a数组末尾添一个极小值,保证不会有前面和他重复的数,那么最后的方案数就是g[n]
记得最长下降子序列的长度要减一
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=5000+10;
int n,ans,maxx;
int a[maxn],g[maxn],f[maxn];
bool vis[maxn*1000];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
n++;
a[n]=-1e9;
for(int i=1;i<=n;++i) f[i]=1;
for(int i=1;i<=n;++i)
for(int j=i-1;j>=1;--j)
if(a[j]>a[i]) f[i]=max(f[i],f[j]+1);
for(int i=1;i<=n;++i) maxx=max(maxx,f[i]);
for(int i=1;i<=n;++i)
{
if(f[i]==1) g[i]=1;
else
{
map<int,bool>vis;
for(int j=i-1;j>=1;--j)
if(a[j]>a[i]&&f[j]==f[i]-1&&!vis[a[j]])
{
vis[a[j]]=1;
g[i]+=g[j];
}
}
}
printf("%d %d",maxx-1,g[n]);
return 0;
}