在还没有编译的时候,我是没有想到能过的,所以说,要有自信。
题链
dp [a][b] 表示以下标为a的那个数为结尾,以b为公差,的等差数列的个数,且等差数列的长度大于等于2
int d=h[i]-h[j];
dp[i][d]=(dp[i][d]+dp[j][d]+1)%mod;
由于数列中的数的大小范围太大,公差也就太大,所以不遍历公差,而是遍历下标,
且下标确定了,公差也就确定了
一、如何确定dp
这里也符合了 当dp表示的情况仍然还有多种情况时,就增加维度。
dp[a]表示以a结尾的等差数列的个数,发现会有不同的公差,并不能写出状态转移方程,当加上一个维度(公差)时,发现情况变少
其实更准确的说,应该是根据状态转移方程的需求来增加维度。
以a结尾的数列,如果倒数第二个数确定了,就不会与其他情况重复。要用以倒数第二个数结尾的数列的个数来推以a结尾的数列的个数,但是以 倒数第二个数 结尾的数列有的公差并不符合,因此我们又要增加一个公差维度维度
二、为什么等差数列的长度大于等于2
首先,状态转移方程在转移的时候,等差数列的长度就一定是大于等于2的。
其次,把长度为1的等差数列算进去,还要处理重复的情况。对于一个dp[i][d],在 j 循环里将可能会出现多次,长度为1的情况却只能加一次,要进行特判。
还有,当i相同,d不相同时,长度为1的数列算进去就重复了。即dp[i][d1]算了数列{ i }的情况,dp[i][d2]算了数列{ i }的情况。(处理倒是可以处理,在加和的时候特判)
go(i,0,n-1)
go(j,0,i-1){
int d=h[i]-h[j];
dp[i][d]=(dp[i][d]+dp[j][d]+1)%mod;
}
题目描述
ljt12138 首先建了 n 个特斯拉电磁塔,这些电塔排成一排,从左到右依次标号为 1 到 n ,第 i 个电塔的高度为 h[i]。
建筑大师需要从中选出一些电塔,然后这些电塔就会缩到地下去。这时候,如果留在地上的电塔的高度,从左向右构成了一个等差数列,那么这个选择方案就会被认为是美观的。
建筑大师需要求出,一共有多少种美观的选择方案,答案模 998244353。
注意,如果地上只留了一个或者两个电塔,那么这种方案也是美观的。地上没有电塔的方案被认为是不美观的。
同时也要注意,等差数列的公差也可以为负数。
输入格式
第一行一个正整数 n 。
第二行 n 个非负整数,第 i 个整数是第 i 个电塔的高度 h[i] 。
输出格式
输出一个整数,表示美观的方案数模 998244353 的值。
#include<iostream>
#include<cstring>
#include<algorithm>
#define go(i,a,b) for(int i=a;i<=b;++i)
#define mod 998244353
using namespace std;
int dp[1000+10][40000+10];
bool v[1000+10][40000+10];
int n,ans;
int h[1000+10];
void getdp(){
go(i,0,n-1){
go(j,0,i-1){
int d=h[i]-h[j];
dp[i][d]=(dp[i][d]+dp[j][d]+1)%mod;
}
go(j,0,i-1){
int d=h[i]-h[j];
if(v[i][d])continue;
ans=(ans+dp[i][d])%mod;
v[i][d]=1;
}
}
}
int main(){
cin>>n;
go(i,0,n-1)scanf("%d",&h[i]);
getdp();
ans=(ans+n)%mod;
printf("%d",ans);
return 0;
}