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;
}
$$Life \quad is \quad fantastic!$$