P4933 大师

题意

给定\(n\)个建筑,第\(i\)高度为\(h[i]\),求拆掉某些建筑后剩下的是等差数列的方案数(对\(998244353\)取模)

\(n\le 1000,h_{max}\le 20000\)

思路

  • \(30pts\)

枚举每个建筑拆不拆

复杂度为\(O(2^{n}*n)\),期望得分$30 $

  • \(60pts\)

枚举等差数列的公差,然后每次扫一下整个序列,看看能否满足,\(h_{max}\le2000\)

上面的是错误的算法,我傻了

但是我们注意到值域\(h_{max}\le 2000\),其实是很小的

大胆猜测枚举公差的方向肯定没错,淦

\(f[i][j]\)表示以\(i\)结尾,公差为\(j\)的等差数列有多少个

每次枚举一个小于\(i\)的位置\(k\),让\(k\)的高度满足\(h[k]=h[i]-j\),然后用\(f[k][j]\)更新\(f[i][j]\)

复杂度为\(O(n^2*h_{max})\),期望得分\(60\)

  • \(100pts\)

考虑优化\(60pts\)

等差数列任意相邻两个数的公差是一定的

我们没必要去枚举公差,直接后面的减去前的就得出来公差了,\(qwq\)

\(f[i][h_i-h_k]=\sum\limits_{k<i}f[k][h[i]-h[k]]+1\)

复杂度为\(O(n^2)\),期望得分\(100\)

\(code\)

/*
@ author:pyyyyyy/guhl37
-----思路------

-----debug-------
注意长度为1的也是等差数列 
*/
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int mod=998244353;
const int N=1000+22;
const int M=20000+2020;
int f[N][M],n,h[N],maxh=0,ans;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;++i) scanf("%lld",&h[i]),maxh=max(h[i],maxh);
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<i;++j)
		{
			int cha=h[i]-h[j];
			if(cha<0) continue;
			f[i][cha]=(f[i][cha]+f[j][cha]+1)%mod;
		}
	}
	for(int i=1;i<=n;++i)
		for(int j=0;j<=maxh;++j) 
			ans=(ans+f[i][j])%mod;
	memset(f,0,sizeof(f));
	for(int i=n;i>=1;--i)
	{
		for(int j=n;j>i;--j)
		{
			int cha=h[i]-h[j];
			if(cha<=0) continue;
			f[i][cha]=(f[i][cha]+f[j][cha]+1)%mod;
		}
	}
	for(int i=1;i<=n;++i)
		for(int j=0;j<=maxh;++j)
			ans=(ans+f[i][j])%mod;
	ans+=n;//!!
	cout<<ans%mod;
	return 0;
}
posted @ 2020-06-14 17:12  pyyyyyy  阅读(284)  评论(0编辑  收藏  举报