CodeForces 1188C/1189F Array Beauty 题解

CF1188C/1189F题目链接

在计算之前,先把数组排序,这样容易计算最小值。为了叙述方便,设 \(A\)\(a_i\) 的最大值。

首先考虑暴力 \(dp\) 。设 \(dp_{i,j,x}\) 表示考虑到第 \(i\) 个位置,当前选择的序列的“美丽值”为 \(j\) ,已经选了 \(x\) 个数。(注: \(x=1\) 时“美丽值”是没有定义的,可以把它看成 \(A\)

\[dp_{i,j,x} \rightarrow dp_{y,min(j,a_y-a_i),x+1} (y > i) \]

时间复杂度? \(O(A n^2 k)\) 。约等于 \(10^{14}\) 。(实际上空间都开不下)

显然要换种方法。

设 $num_{i} $ 为最小值等于 \(i\) 的子序列数量,明显可以通过这个算出所有总和。但是直接计算还比较麻烦,就用容斥, 先计算"美丽值"大于等于 \(i\) 的序列数量 \(num'_i\) ,再计算\(num_i = num'_i - num'_{i+1}\)

关键就是计算 \(num'_i\) 。当然dp: \(dp_{x,j}\) 表示已经考虑到第 \(x\) 个位置,已经选了 \(j\) 个数,且符合条件的方案数。

则 $$dp_{x,j} =\sum\limits_{y=1}^{l_x}dp_{y,j-1}$$ 其中 \(l_x\) 是最大的下标,使得\(a_x - a_{l_x} \geq i\)

\(l_x\) 可以每次往左推(它明显是不增的)

当然前缀和优化,\(sum_{x,j}\)\(\sum\limits_{y=1}^{x}dp_{y,j}\)

\(dp_{x,j} = sum_{l_i,x}\)\(sum_{x,j} = sum_{x-1,j} + dp_{x,j}\)

也可以把 \(dp\) 数组去掉,得 $$sum_{x,j} = sum_{x-1,j} + sum_{l_x,j-1}$$

这样dp就可以了。但是下面还有一个重点:

\(dp\)复杂度是 \(O(nk)\) 。如果从 \(1\)\(A\) 枚举 \(i\) ,总时间复杂度就达到了 \(O(Ank)\) ,直接挂了

然而有个看似不起眼的优化:长度为 \(k\) 的序列,有 \(k-1\)\(a_j - a_{j-1}\) 需要一起取 \(min\) (这里设 \(a\) 是递增的,且均不小于 \(0\) )。而 \(a_k\) 又只能是 \(A\) ,容易发现:这样的序列,"美丽值"不会超过 \(\left\lfloor\dfrac{A}{k-1}\right\rfloor\)\(i\) 只需枚举到这个数。

然后你发现:复杂度降到了 \(O(\dfrac{A}{k-1} \times nk)\) ,即 \(O(An)\) ,可以过了

这个自己几乎都没想出来。。这可能就是难度 \(2700\)(upd:现在调整成\(2500\)了) ,但通过人数高达近 \(800\) 的原因了。

最后我忘改模数还被坑了一次提交

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
const int inf=0x3f3f3f3f,mod=998244353;
const double pi=3.1415926535897932,eps=1e-6;
#define check(x) if(x>=mod) x-=mod
int n,a[1005],sum[1005][1005],k,l[1005],mx,ans,res[100005];
int main()
{
    scanf("%d%d",&n,&k);
    rep(i,1,n) scanf("%d",a+i),mx = max(mx,a[i]);
    sort(a+1,a+n+1);
    int lim = mx / (k-1);
    rep(i,1,n) l[i] = i - 1;
    rep(i,1,lim) {
        rep(j,1,n) {
            while(l[j] > 0 &&a[j] - a[l[j]] < i) l[j]--;
        }
        sum[0][0] = 1;
        rep(j,0,k)
        rep(x,1,n) {
            sum[x][j] = sum[x - 1][j];if(j) sum[x][j] += sum[l[x]][j - 1];
            check(sum[x][j]);
        }
        res[i] = sum[n][k];
    }
    rep(i,1,lim) {
        ans += (ll)i * (((ll)res[i] - res[i+1] + mod)%mod) % mod;
        check(ans);
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2020-03-03 12:51  beacon_cwk  阅读(216)  评论(0)    收藏  举报