P1108 低价购买

https://www.luogu.com.cn/problem/P1108

\(f_i,g_i\) 表示以 \(i\) 结尾的最长下降子序列最大长度,和以 \(i\) 结尾、以 \(f_i\) 为长的不重复子序列方案数。这里的不重复不仅要在结尾为 \(i\) 的集合中不重,而且跟 \(1\sim i-1\) 的也不重复。
首先可以发现,\(f_i\) 一定不能从 \(\le p\)\(j\) 转移过来,\(p\) 表示 \(a_i\) 之前第一个等于 \(a_i\) 的数的下标。原因是 \(p\) 能统计的 \(i\) 也能统计,就会重复了,所以不能从 \(\le p\) 的转移,也不需要。
同时我们发现,符合要求的子序列的开头数字 \(x\) 一定是 \(x\) 第一次出现的地方。或者说,我们不妨令所有符合要求的子序列的开头数字都要这样,容易发现这个要求不会影响答案。

#include <bits/stdc++.h>
using namespace std;
const int N=5e3+5,V=(1<<16)+5;
int n,a[N],f[N],g[N],bk[V];
bool isfi[N];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		isfi[i]=!bk[a[i]];
		bk[a[i]]++;
	}
	for(int i=1;i<=n;i++){
		f[i]=g[i]=1;
		for(int j=i-1;j&&a[j]!=a[i];j--){
			if(a[j]>a[i]){
				if(f[j]+1>f[i]){
					f[i]=f[j]+1;
					g[i]=g[j];
				}
				else if(f[j]+1==f[i])g[i]+=g[j];
			}
		}
		if(f[i]==1&&!isfi[i])g[i]=0;
	}
	int id=0,sum=0;
	for(int i=1;i<=n;i++){
		if(f[id]<f[i])id=i,sum=g[i];
		else if(f[id]==f[i])sum+=g[i];
	}
	cout<<f[id]<<' '<<sum;
}

upd:现在觉得好naive

posted @ 2021-11-10 13:29  pengyule  阅读(15)  评论(0编辑  收藏  举报