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\) )。
方案数为:
若 \(2p\le n\) ,则不存在第二段的限制,方案数为:
接下来把重点放在快速计算第一种情况的答案上,继续推式子。
先把限制变个形:
枚举 \(T=p+c\) :
里面是一个简单的上指标求和:
时间复杂度 \(\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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17110160.html
浙公网安备 33010602011771号