zoukankan      html  css  js  c++  java
  • P4933 大师


    题解

    简化题面

    给你一个长度为 (n) 的序列,问这个序列中有多少个等差序列。

    思路

    能把题目简化出来这一步很关键。
    这种计数类问题我们一般用 (dp) 来解决。
    (dp[i][j]) 表示前 (i) 个数中以 (a[i]) 结尾的公差为 (j) 的等差序列有多少。
    转移的话我们可以枚举 (j(j<i)),再枚举公差 (k),那么就有 (dp[i][k]=sum_{j=1}^{i-1}(dp[j][k]+1)),其中 (a[i]-a[j]=k)
    (dp[j][k]+1) 中的 (+1) 是为了处理 (i) 之前只有 (a[j])(a[i]) 两个数构成的等差数列。
    时间复杂度 (O(n^2v)),无法通过此题,我们需要考虑优化。
    仔细想想其实我们可以不用枚举公差的,因为 (k=a[i]-a[j]) 才能转移,所以我们可以让公差完全由 (a[i])(a[j]) 来决定,新的转移方程如下:
    (dp[i][a[i]-a[j]]=sum_{j=1}^{i-1}(dp[j][a[i]-a[j]]+1))
    注意到公差可能是负数,所以我们在处理第二维的时候都加上一个最大高度 (H) 来保证第二维是正的:
    (dp[i][a[i]-a[j]+H]=sum_{j=1}^{i-1}(dp[j][a[i]-a[j]+H]+1))
    时间复杂度 (O(n^2))

    答案统计

    我们枚举 (i,k),对所有的 (dp[i][k]) 求和就是答案了,时间复杂度 (O(nv))
    但是我们可以优化。
    看到上面的转移式子 (dp[i][a[i]-a[j]+H]=sum_{j=1}^{i-1}(dp[j][a[i]-a[j]+H]+1))
    其实我们可以在算 (dp[i][a[i]-a[j]+H]) 的时候一块把方案数算到答案里去,不必再算完 (dp[i][a[i]-a[j]+H]) 后再枚举统计。
    在上面考虑的过程中,我们是忽略了单元素构成等差数列的情况,所以在答案中我们要把它们加进去。

    (Code)

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int N=1005;
    const int M=998244353;
    int n,H,ans;
    int a[N],dp[N][50005];                //dp数组的第二维要开两倍  
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		H=max(H,a[i]);        //求最大高度 
    	}
    	for(int i=2;i<=n;i++)         //注意从第二项开始 
    	{
    		for(int j=1;j<i;j++)
    		{
    			dp[i][a[i]-a[j]+H]=(dp[i][a[i]-a[j]+H]+dp[j][a[i]-a[j]+H]+1)%M;  //转移方程,注意第二维要加最大高度H保证为正 
    		    ans=(ans+dp[j][a[i]-a[j]+H]+1)%M;   //我们在算的过程中一起统计答案就好了 
    		}
    	}
    	printf("%d
    ",(ans+n)%M);     //最后别忘了有n种由单元素构成的等差序列 
    	return 0;
    }
    
  • 相关阅读:
    DNS原理入门
    软件架构入门
    熵:宇宙的终极规则
    新鲜事
    加密货币的本质
    汇编语言入门教程
    HTML & CSS
    [模板] 矩阵快速幂
    [模板] 三分
    [模板] 2-SAT 问题
  • 原文地址:https://www.cnblogs.com/xcg123/p/13449245.html
Copyright © 2011-2022 走看看