两个常见模型
bzoj4321
编号为1~n的人排成一排,问有多少种排法使得任意相邻两人的编号之差不为1或-1。
n<=1000
破坏空位:有两个相邻的之间
我们设(f[i][j])表示1~i的排列,有j组相邻的相差1,且i和i-1不相邻的方案数;
我们设(g[i][j])表示1~i的排列,有j组相邻的相差1,且i和i-1相邻的方案数;
考虑插入i+1的位置,有:
不破坏空位且不与 i 相邻、不破坏空位且与 i 相邻、破坏空位且不与 i 相邻、破坏空位且与 i 相邻(只发生在 g 的转移) 4种。
分别推一下方案数:
首先考虑i和i-1相邻的方案数:
我们考虑一下i-2,i-1和i的关系:
- i-1与i-2相邻,然后我们将i放在i-1与i-2的中间:这样将i-1,i-2这一对相邻拆开了,但是又增加了i和i-1这一对,所以相邻的个数相当于没增也没减,也就是(g[i][j])有一项是从(g[i-1][j])转移过来的(前i-1个数,i-1和i-2相邻,共有j对相邻)
- i-1与i-2相邻,但是i不放在i-1与i-2的中间,而放在i-1的另一侧:这样我们没有破坏i-1和i-2这一对相邻,并且新增加了i-1和i这一对相邻,因此相邻数增加了1,转移后是j,那么转移前应该是j-1:(g[i-1][j-1])
- i-1与i-2不相邻:那么我们也就可以将i放在i-1的一侧,这样增加一对相邻,由于可以将i放在i-1左右两边共两种情况,所以:(f[i-1][j-1]*2)因为i-1和i-2不相邻嘛,所以用f;
(g[i][j]=g[i-1][j]+g[i-1][j-1]+f[i-1][j-1]*2)
同理可以推出(f[i][j])的转移方程:
- 本来i-1与i-2相邻,将i插入j对相邻的数的任意一对,这样就破坏了一对,这样方案来源于(g[i-1][j+1]),有(j+1-1)种位置可以选(i-1与i-2那对不能拆,因为插入又会形成新的)。
- 本来i-1与i-2不相邻,将i插入j对相邻的数的任意一对,这样就破坏了一对,这样方案来源于(f[i-1][j+1]),有(j+1)种位置可以选。
- 又可能i不去拆开相邻的数,就可以来源于 (g[i-1][j])*(i-j-1)(可以插入i-1与i-2,不改变对数) 或 (f[i-1][j])*(i-j-2)
(f[i][j]=g[i-1][j+1]*j+f[i-1][j+1]*(j+1)+g[i-1][j]*(i-j-1)+f[i-1][j]*(i-j-2))
最后的答案就是(f[n][0])
时间复杂度 O(n^2) 。
BZOJ2560 串珠子
有n个珠子,第i和j个珠子之间有(c[i][j])条不同的绳子可选。每对珠子之间可以选择不连绳子,也可以选择用其中一种绳子连接。
问有多少种方案能使n个珠子成为连通图。
n<=16
连通图计数套路: 用总数减去不连通的方案数,而不连通的方案数,可以枚举1号点所在连通块的点集(有的问题中是大小) ,用这个点集的连通方案数乘以剩余点集的总方案数即可。???
我们设两个数组,g[s]表示s状态下的所有情况,即s状态下的点两两之间任意连边(包括不连边的情况),f[s]表示s状态下的合法情况,即使得s状态下所有点连通的合法情况。那么答案就是f[2^n-1]。
(g[s]=g[s-(1<<i)]+sum_{jin s-(i<<i)}c[i][j]) (O(n*2^n))
然后我们可以很容易地求出g[s],我们求出g[s]后,考虑如何求f[s]。
先定一个基准点 x 和基准点相连的都是合法的, 其余集合t=s ^ (1<<(x-1))可以随便连; f[s]就是g[s]减去所有的不合法情况。那么我们可以枚举s的所有子集,设子集为i,那么不合法的情况就是g[i]*f[s^ i](s状态下,其中s^i的点是合法的,然后剩余的点随意连边的方案数是g[i],这样保证子集i和s ^i不连通,并且子集s ^i内部是联通的,方案数不合法方案数就是g[i]*f[s ^i] 那么,我们减去这些情况,就能求出f[s]了。
(f[i]=g[i]-sum_{t的所有子集i} f[s xor i]*g[i];)