[CF1528B]Kavi on Pairing Duty

[CF1528B]Kavi on Pairing Duty

壹、题目描述 ¶

\(2n\) 个点在一条直线上,对于第 \(i\) 个点,满足 \(x_i=i\),你需要把他们两两配对,为了方便叙述,我们定义一个匹配 \(i\) 左边的点为 \(l_i\),右边的点为 \(r_i\). 一种配对方案是好的当且仅当对于任意两个配对 \(i,j\),满足下列条件中的一个:

  • 两个匹配的长度一样,即 \(r_i-l_i+1=r_j-l_j+1\)
  • 一个匹配完全包含另外一个匹配,即 \(l_i<l_j<r_j<r_i\;∨\;l_j<l_i<r_i<r_j\)

给定 \(n\le 10^6\),求好的匹配的方案数。

贰、题解 ¶

我们将配对看成连线,就像原题中给的图一样。

考虑定义 \(f_i\) 为有 \(2i\) 个点时的好的匹配方案数,显然答案为 \(f_n\). 问题来到如何求 \(f_i\).

定义 \(x\)\(1\) 号点所匹配的点,那么,对于所有 \(p\in[x+1,2n]\) 都有 \(p\) 所属配对的长度都为 \(\lang 1,x\rang\) 配对的长度,即 \(x\).

证明十分简单,因为 \(p\) 已经在 \([1,x]\) 之外了,所以 \(p\) 所属的配对不可能与 \(\lang 1,x\rang\) 满足第二个条件,那么就只有可能满足第一个条件了。

现在,我们对于 \(x\) 分类进行讨论:

  • \(x\le n\),那么所有 \([x+1,2n]\) 中的点的长度都得是 \(x\),而 \((n,2n]\) 显然是 \([x+1,2n]\) 的子集,这说明所有的配对长度都得是一样的,这只有当 \(x\mid n\) 时满足,故这种情况的方案数为 \(D(n)\),即 \(n\) 的因数个数;
  • \(x>n\) 时,对于所有 \(i\in[x+1,2n]\) 的配对,都只有可能是 \(i-x+1\),因为需要满足长度相等,且配对的点在 \([1,2n]\) 之间,也就是说,对于所有 \([x,2n]\) 的点和 \([1,2n-x]\) 配对了,剩下的就是 \([2n-x+1,x-1]\) 的点自由配对,一共有 \(2x-2n-2\) 个点,也就是 \(f_{x-n-1}\),又 \(x\in [n+1,2n]\),那么有 \(x-n-1\in [0,n-1]\),这种情况就是 \(\sum_{i=0}^{n-1}f_i\),而 \(f_0=0\),我们可以进一步化简为 \(\sum_{i=1}^{n-1}f_i\)

综上,构成 \(f_i\) 的有两个部分:

  • \(D(i)\),即 \(i\) 的因数个数;
  • \(\sum_{k=1}^{i-1}f_k\)

计算 \(D(i)\) 可以使用埃氏筛法,时间复杂度 \(\mathcal O(n\log n)\)

叁、参考代码 ¶

#include<cstdio>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;

// #define NDEBUG
#include<cassert>

#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline T readin(T x){
    x=0; int f=0; char c;
    while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
    for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
    return f? -x: x;
}
template<class T>inline void writc(T x, char s){
    static int fwri_sta[1005], fwri_ed=0;
    if(x<0) putchar('-'), x=-x;
    do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
    while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
    putchar(s);
}

const int mod=998244353;
const int maxn=1e6;

int f[maxn+5], n;

signed main(){
    n=readin(1);
    rep(i, 1, n) for(int j=i; j<=n; j+=i) ++f[j];
    int tmp=0;
    rep(i, 1, n){
        f[i]=(f[i]+tmp)%mod;
        tmp=(tmp+f[i])%mod;
    }
    writc(f[n], '\n');
    return 0;
}

肆、关键 の 步骤 ¶

挖掘特性是十分重要的。

这道题比较关键的地方在于当 \(1\) 与某个点进行匹配之后,有一些点所属的配对就也被固定了。

一般地,对于任意 \(l,r\) 的配对,在 \([1,l-1]\)\([r+1,2n]\) 的点都不可能与它们满足包含关系了,而当 \(l=1\) 或者 \(r=2n\) 时,这两个区间实际上就只有一个有定义了。所以实际上我们找的时这个特性的特殊情况,特性的特性?

posted @ 2021-05-26 17:46  Arextre  阅读(111)  评论(0编辑  收藏  举报