数轴上连 n 条弦,共有 k 个交点的方案数

Posted alfalfa_w

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数轴上连 n 条弦,共有 k 个交点的方案数相关的知识,希望对你有一定的参考价值。

问题

数轴上有 \\(2n\\) 个点,考虑所有用 \\(n\\) 条弦将它们完美匹配的方案,有多少种方案会产生 \\(k\\) 个交点(不考虑三线共点)?

在 OEIS 上为A067311

分析

本文主要是对这篇论文的翻译。

转化问题

首先我们考虑 DP。考虑生成函数,我们用 \\(z\\) 累计交点数。记 \\(F_i,j\\) 为考虑了前 \\(i\\) 个点,往后连了 \\(j\\) 条弦的生成函数。那么一条弦在 DP 中经历 \\(2\\) 个过程:“产生”和“闭合”,我们在闭合时累加交点数。假设往后连的 \\(j\\) 条弦中,我们选择从左往右第 \\(a\\) 条弦在 \\(i+1\\) 处闭合,那么会有 \\(z^j-a\\) 的贡献。因此,闭合的总贡献就是 \\(1+z+\\cdots+z^j-1 = (1-z^j)/(1-z)\\)。无论 DP 的路径如何,\\((1-z)^-1\\) 总是会乘恰好 \\(n\\) 次,所以可以留到最后再乘。我们可以写出 DP 的转移式:

\\[F_i,j = F_i-1,j-1+ (1-z^j+1)F_i-1,j+1 \\]

答案为 \\((1-z)^-nF_2n,0\\)

考虑把 DP 状态看作点,转移的路径看作折线,那么相当于枚举所有折线,对于一条折线,设所有\\单位线段的“高度”为 \\(h_1,h_2,\\cdots,h_n\\),那么它对答案有 \\(\\prod (1-z^h_i)\\) 的贡献。

考虑把折线与有根树的 DFS 过程一一对应:/单位线段看作入栈操作,\\单位线段看作出栈操作。那么,树的每个非根结点都可能为“普通点”或“关键点”,对于一棵有 \\(r\\) 个关键点,关键点深度和为 \\(k\\) 的树,它对答案的贡献为 \\((-1)^rz^k\\)

通过构造对合来抵消系数

对于一棵树,我们只保留所有关键点到根的路径的并(虚树),假设还剩下 \\(l\\) 条边。对于剩下 \\(l\\) 条边的虚树,考虑把它补全的方案数。相当于在原本的入栈-出栈序列中的 \\(2l+1\\) 个空隙中分别插入合法括号序列,把长度补到 \\(2n\\) 的方案数。又相当于长度为 \\(2n+2l\\) 的括号序列,满足最后 \\(2l\\) 个位置均为\')\'的方案数(考虑后 \\(2l\\) 个位置的匹配位置,将序列分成 \\(2l+1\\) 段)。可以使用反射法,方案数为 \\(\\binom2nn+l - \\binom2nn+l+1\\)

考虑两棵虚树,它们的关键点个数、关键点深度和、边数分别为 \\((r,k,l)\\)\\((r+1,k,l)\\),那么它们的贡献会抵消。

让我们重新列举一下合法虚树需要满足的条件:

  • 结点无标号,但可能为“关键点”;
  • 所有叶子必为“关键点”;
  • 所有“关键点”的深度之和为 \\(n\\) \\((n\\ge 1)\\)
  • 结点的儿子之间有先后顺序。

因为所有叶子都是关键点,所以在 DFS 的过程中,如果出现入栈之后立即出栈的情况,则一定访问到了关键点。

我们可以按 DFS 中的出栈顺序给“关键点”标上 \\(1..k\\) 的编号。

设在“关键点” \\(i\\) 出栈前,入栈、出栈操作分别进行了 \\(a_i,b_i\\) 次。可以观察到下列性质:

  • \\(\\sum_i=1^k b_i-a_i = n\\)
  • \\(1\\le a_1\\le a_2\\le \\cdots\\le a_k\\)
  • \\(0= b_1 < b_2 < \\cdots < b_k\\)
  • \\(b_i<a_i\\) \\((\\forall 1\\le i\\le k)\\)
  • \\(b_i+1\\le a_i\\) \\((\\forall 1\\le i \\le k)\\)
  • 树的边数是 \\(a_k\\)
  • 合法的序列 \\((a_1,b_1), (a_2,b_2), \\cdots, (a_k,b_k)\\) 与合法的树构成双射。

为了方便表示,我们把合法序列画成方格图:假设有一个向右、向上无限延伸的方格图,我们只保留最下面的 \\(k\\) 行,对于从下往上的第 \\(i\\) 行,我们只保留第 \\(b_i+1\\) 到第 \\(a_i+1\\) 个格子。这样,画出的方格图是联通的。

为了体现 \\(b_k<a_k\\) 这条性质,我们在方格图第 \\(k\\) 行第 \\(a_k+1\\) 个格子上方添加一个格子。

考虑按照下图中的方法构造对合:

这样,在大部分情况中,我们总能把合法的方格图完美匹配,每对匹配的宽度相同、高度差 \\(1\\),这使得它们在答案的式子中抵消了。所有不能被匹配的方格图都属于下面这种情况:

因此,原答案和只保留这种情况的答案是一样的。答案为:

\\[(1-z)^-n\\sum_i=0^n (-1)^i \\left(\\binom2nn+l-\\binom2nn+l+1\\right) z^\\binomi+12 \\]

也可以写成:

\\[(1-z)^-n\\sum_i=-n^n (-1)^i \\binom2nn+l z^\\binomi+12 \\]

2019.8.31 互不侵犯

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

注:数据有加强(2018/4/25)

输入格式

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出格式

所得的方案数

输入输出样例

输入 #1
3 2
输出 #1
16
题目来源洛谷P1896/四川省选

有点像八皇后 但是看数据范围应该可以用状压dp
上代码
#include<iostream>
#include<cstdio>
using namespace std;
long long n,k,dp[15][2050][205],sc[2050],num[2050];
long long ans,cnt;
long long getnum(long long x)

    long long res=0;
    while(x)res+=(x&1),x>>=1;
    return num[(int)cnt]=res;

int main()

    scanf("%lld%lld",&n,&k);
    long long maxn=(1<<n)-1;
    for(long long i=0;i<=maxn;i++)
        if(!(i&(i<<1)))sc[++cnt]=i,dp[1][cnt][getnum(i)]=1;
    for(long long i=2;i<=n;i++)
    
        for(long long j=1;j<=cnt;j++)
        
            for(long long l=1;l<=cnt;l++)
            
                long long x=sc[j],y=sc[l];
                if(x&y)continue;
                if(x&(y<<1))continue;
                if(x&(y>>1))continue;
                for(long long m=0;m<=k;m++)dp[i][j][m+num[j]]+=dp[i-1][l][m];
            
        
    
    for(long long i=1;i<=cnt;i++)ans+=dp[n][i][k];
    printf("%lld",ans);
    return 0;

 

以上是关于数轴上连 n 条弦,共有 k 个交点的方案数的主要内容,如果未能解决你的问题,请参考以下文章

互不侵犯

bzoj 1856 [SCOI2010]生成字符串

1~n这n个正整数中恰有k个质数

BZOJ1087状压DP 解题报告

2019.8.31 互不侵犯

dp--状压dp