Codeforces1189 F. Array Beauty(dp,优化)

题意:

定义一个序列的美丽值序列中任意两个数差值的最小值
给定长度为n的序列a,给定k,要求计算所有长度为k的子序列的美丽值的和,
答案对998244353取模

数据范围:n,k<=1e3,0<=a[i]<=1e5

解法:

由于题目美丽值和数的顺序无关,可以先对a数组从小到大排序,
那么子序列的美丽度就是对所有相邻数的差值取min,

因为a[i]<=1e5,那么美丽值最大不超过1e5,
假设:
美丽值=1的子序列数量为s[1]
美丽值=2的子序列数量为s[2]
...

美丽值为1的子序列对答案的贡献为s[1]*1
美丽值为2的子序列对答案的贡献为s[2]*2
美丽值为3的子序列对答案的贡献为s[3]*3
设s[]的后缀和为ss[]
那么s[1]*1+s[2]*2+s[3]*3...=ss[1]+ss[2]+ss[3]...

ss[]是s[]的后缀和,同时ss[i]也表示满足美丽值>=i的方案数
尝试计算ss[]:
对于一个美丽值x,满足子序列相邻元素差值都>=x的方案数就是ss[x]

枚举x,对于每一个x单独计算:
令d[i][j]为前i个数,选j个且以i结尾,差值>=x的方案数
那么转移方程为d[i][j]=sum(d[k][j-1]),其中a[i]-a[k]>=x
因为满足a[i]-a[k]>=x的k一定是连续的,且k是递增的
所以可以用一个指针维护这一段的右边端点(左端点为1),同时维护前缀和
这样sum(d[k][j-1])转移就是O(1)的了,dp过程复杂度为O(n*k)
本次计算出的d[n][k]就是ss[x]

x范围最大a[n],那么枚举x+dp的总复杂度为O(n*k*a[n]),还需要优化

一个dp范围优化:
长度为k的合法x子序列,需要满足k-1对相邻数都>=x,
那么在这个子序列中,第k个数和第1个数的差值>=(k-1)*x
考虑x取值的最大范围:
第k个数和第1个数的最大差值显然为a[n]-a[1],
那么a[n]-a[1]>=(k-1)*x,移项一下变为x<=(a[n]-a[1])/(k-1),
(a[n]-a[1])/(k-1)粗略当成a[n]/k
那么总复杂度就不是O(n*k*a[n]),而是O(n*k*(a[n]/k))=O(n*a[n])


ps:
我的代码是对于d[i][j]的每一个j开了一个指针pos[j],
维护满足a[i]-a[k]>=x的k的最右位置,
同时还开了sum[][]维护d[][]的前缀和,
dp的时候,第一层枚举i,第二层枚举j.

但是看了其他人的代码,似乎是第一层枚举j,第二层枚举i
这样的话对于每一层j,维护sum(d[k][j-1])只用一个变量指针(而不是我的数组),
这样似乎会更快一点?(别人500+ms,1900+ms)

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
const int mod=998244353;
int sum[maxm][maxm];
int d[maxm][maxm];
int pos[maxm];
int a[maxm];
int n,k;
signed main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    int ans=0;
    for(int x=1;x*(k-1)<=a[n]-a[1];x++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=k;j++){
                d[i][j]=0;
                sum[i][j]=0;
            }
        }
        for(int j=1;j<=k;j++){
            pos[j]=0;
        }
        for(int i=1;i<=n;i++){
            d[i][1]=1;
            sum[i][1]=sum[i-1][1]+d[i][1];
            for(int j=2;j<=k;j++){
                while(a[i]-a[pos[j]+1]>=x){
                    pos[j]++;
                }
                d[i][j]=sum[pos[j]][j-1];
                sum[i][j]=sum[i-1][j]+d[i][j];
                sum[i][j]%=mod;
            }
        }
        for(int i=k;i<=n;i++){
            ans+=d[i][k];
            ans%=mod;
        }
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107777918