peiwenjun's blog 没有知识的荒原

CF1785F Minimums or Medians 题解

题目描述

给定 \(n\) ,初始 \(S=\{1,2,\cdots,2n\}\)

你可以进行 \(k\) 次操作,每次删掉 \(S\) 中最小的两个数,或删掉 \(S\) 的两个中位数。

求能得到的本质不同集合\(S\)的个数。

数据范围

  • \(1\le k\le n\le 10^6\)

时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{512MB}\)

分析

我们希望找到 \(S\) 合法的充要条件。

注意到第 \(i\) 次操作前,\(S\) 中较大的中位数一定为 \(n+i\)

观察 \(S\) 中被删除的所有数,有两个显然的性质:

  • 每一个连续段长度均为偶数,总长度为 \(2k\)
  • 值域 \(\in[1,n+k]\)

钦定开头一段(即从 \(1\) 开始的连续段,可能不存在)用操作一解决,其它连续段仅能使用操作二。

但这并不足以刻画 \(S\) ,注意到除开头外,至多有一个连续段包含 \(\le n\) 的数。

具体的,若 \(x\le n\) 在连续段中,由于连续段的中位数 \(\ge(n,n+1)\) ,因此 \(x\sim n+(n+1)-x\) 在同一个连续段中。

据此我们可以得到 \(S\) 的第三个限制:

  • 记不在开头的最小的在连续段中的数为 \(x\) ,则 \([x,2n+1-x]\) 在同一个连续段中。

容易证明这是充要条件,构造方案如下:

对于除了开头的每个连续段,当集合 \(S\) 的(两个)中位数的平均值和连续段中位数平均值相同时,连续执行若干次操作二删空这个连续段,否则执行操作一。


接下来考虑如何计数。

\(f(n,m)\) 为从 \(n\) 个数中选 \(2m\) 个数,每个连续段长度都是偶数的方案数。

考虑给一个 \(1\times(n-m)\) 的长方形黑白染色,其中恰有 \(m\) 个黑色格子。

容易发现 \(f(n,m)\) 中的每种方案,将每个连续段长度折半,恰好能够唯一映射到一种染色方式。

因此 \(f(n,m)=\binom{n-m}m\)

记第一段长度为 \(2p\)

\(2p\lt n\) ,假设第二段在 \(n\) 左侧长度为 \(c\) ,限制为 \(2p+2c\le 2k\) (总长度不能超过 \(2k\) )并且 \(n-c+1\gt 2p+1\) (不能覆盖到 \(2p+1\) )。

方案数为:

\[\begin{aligned} &\sum_{p=0}^k[2p\lt n]\sum_{c=0}^{\min(k-p,n-2p-1)}f(k-c,k-p-c)\\ =&\sum_{p=0}^k[2p\lt n]\sum_{c=0}^{\min(k-p,n-2p-1)}\binom p{k-p-c}\\ \end{aligned} \]

\(2p\le n\) ,则不存在第二段的限制,方案数为:

\[\begin{aligned} &\sum_{p=0}^k[2p\ge n]f(n+k-2p-1,k-p)\\ =&\sum_{p=0}^k[2p\ge n]\binom{n-p-1}{k-p}\\ \end{aligned} \]

接下来把重点放在快速计算第一种情况的答案上,继续推式子。

先把限制变个形:

\[\begin{cases} p\le k,2p\lt n\\ c\le k-p\\ c\le n-2p-1\\ \end{cases} \Rightarrow \begin{cases} p+c\le k\\ p+c\le n-p-1\\ \end{cases} \]

枚举 \(T=p+c\)

\[ans=\sum_{T=0}^k\sum_{p=0}^{\min(n-T-1,T)}\binom p{k-T}\\ \]

里面是一个简单的上指标求和:

\[ans=\sum_{T=0}^k\binom{\min(n-T-1,T)+1}{k-T-1} \]

时间复杂度 \(\mathcal O(n)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5,mod=998244353;
int k,n,res;
int fac[maxn],inv[maxn];
int qpow(int a,int k)
{
    int res=1;
    while(k)
    {
        if(k&1) res=1ll*res*a%mod;
        a=1ll*a*a%mod,k>>=1;
    }
    return res;
}
int c(int n,int m)
{
    if(m<0||n<m) return 0;
    return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void add(int &x,int y)
{
    if((x+=y)>=mod) x-=mod;
}
int main()
{
    scanf("%d%d",&n,&k),fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
    inv[n]=qpow(fac[n],mod-2);
    for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1ll)%mod;
    if(n==k) printf("1\n"),exit(0);
    for(int i=0;i<=k;i++) add(res,c(min(n-i-1,i)+1,k-i+1));
    for(int i=(n+1)/2;i<=k;i++) add(res,c(n-i-1,k-i));
    printf("%d\n",res);
    return 0;
}

posted on 2023-02-10 19:45  peiwenjun  阅读(9)  评论(0)    收藏  举报

导航