[Luogu P4933] 大师

\(\text{Problem}\)题目链接

\(\text{Solution}\)

发现本题值域很小,可以考虑从值域入手解决问题。记 \(dp_{i,j}\) 表示以 \(i\) 位置结尾且公差为 \(j\) 的等差数列个数(注意公差为负数的情况),则状态转移方程如下:

\[\qquad dp_{i,j}=\sum\limits_{1\leq k < i,a_{i}-a_{k}=j}dp_{k,j} \qquad \]

时间复杂度为 \(O(n^2v)\)\(v\) 为值域。

考虑优化这个 \(dp\) 过程,发现上述 \(dp\) 过程中,公差 \(j\) 不改变,故可以考虑枚举公差 \(j\),把空间压缩到 \(O(n)\)。考虑维护当 \(j\) 被固定 ,最后一个位置的值为 \(x\) 时,则可以用 \(f_{x}\) 表示等差数列最后一个数为 \(x\) 时的方案数之和。则 \(dp_{i}=f_{a_{i}-j}\),于是转移的时间复杂度优化为 \(O(n)\),总时间复杂度为 \(O(nv)\),总空间复杂度为 \(O(n+v)\)

\(\text{Code}\)

#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
#define int long long
//#define double long double
using namespace std; const int N=1010, Mod=998244353;
inline int read()
{
    int s=0, w=1; ri char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
    return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,a[N],dp[N],qz[60010],res;
signed main()
{
    n=read();
    for(ri int i=1;i<=n;i++) a[i]=read(), res++;
    for(ri int i=-2e4;i<=(int)2e4;i++)
    {
        for(ri int j=1;j<=n;j++) qz[a[j]+20001]=0;
        for(ri int j=1;j<=n;j++) (dp[j]=qz[a[j]-i+20001])%=Mod, (qz[a[j]+20001]+=dp[j]+1)%=Mod, (res+=dp[j])%=Mod;
    }
    printf("%lld\n",res);
    return 0;
}
posted @ 2020-08-04 21:00  zkdxl  阅读(83)  评论(0编辑  收藏  举报