[CF838D]Airplane Arrangements

280 篇文章 1 订阅

题目

传送门 to luogu

题目概要
n n n 个座位、 m m m 个人,每个人依次选择一个心仪座位与方向,找到该方向上与心仪座位最近的空座位并坐下。如果有人没有座位,他就会愤怒地吃掉 s y sy sy 的网盘!

为了全世界男人的福音,请你保护 s y sy sy 的网盘——求出有多少种情况能够保全非物质文化遗产。两种情况不同,当且仅当某个人的心仪座位发生了改变,或者更换了方向。

数据范围与提示
1 ≤ m ≤ n ≤ 1 0 6 1\le m\le n\le 10^6 1mn106

∗ \color{red}^{*} 尝试

我™直接不会做,于是选择打表。大概输出了这样一个东西,剩下的没啥参考价值了——不难发现,答案已经超过 i n t \tt int int 范围了。行号为 n n n ,列号为 m m m ,均从 1 1 1 开始。

2
4 12
6 32 128
8 60 400 2000
10 96 864 6912 41472
12 140 1568 16464 153664 1075648
14 192 2560 32768 393216 4194304 33554432
16 252 3888 58320 839808 11337408 136048896 1224440064
18 320 5600 96000 1600000 25600000 384000000 825032704

g ( n , m ) g(n,m) g(n,m) 表示方案数,容易看出 g ( n , 1 ) = 2 n g(n,1)=2n g(n,1)=2n g ( n , 2 ) = 4 n 2 − 4 g(n,2)=4n^2-4 g(n,2)=4n24 。于是我大胆猜想,是一个 m m m 次的关于 n n n 的多项式。说干就干,打了一个高斯消元,又找到
g ( n , 3 ) = 8 n 3 − 24 n − 16 g ( n , 4 ) = 16 n 4 − 96 n 2 − 128 n − 48 g ( n , 5 ) = 32 n 5 − 320 n 3 − 640 n 2 − 480 n − 128 g(n,3)=8n^3-24n-16\\ g(n,4)=16n^4-96n^2-128n-48\\ g(n,5)=32n^5-320n^3-640n^2-480n-128 g(n,3)=8n324n16g(n,4)=16n496n2128n48g(n,5)=32n5320n3640n2480n128

看看这些系数,发现它们是 2 m 2^m 2m 的倍数。故而除掉,再看一次完整版。
{ g ( n , 1 ) 2 = n 1 + 0 g ( n , 2 ) 4 = n 2 + 0 n − 1 g ( n , 3 ) 8 = n 3 + 0 n 2 − 3 n 1 − 2 g ( n , 4 ) 16 = n 4 + 0 n 3 − 6 n 2 − 8 n 1 − 3 g ( n , 5 ) 32 = n 5 + 0 n 4 − 10 n 3 − 20 n 2 − 15 n 1 − 4 \begin{cases} {g(n,1)\over 2}=n^1+0\\ {g(n,2)\over 4}=n^2+0n-1\\ {g(n,3)\over 8}=n^3+0n^2-3n^1-2\\ {g(n,4)\over 16}=n^4+0n^3-6n^2-8n^1-3\\ {g(n,5)\over 32}=n^5+0n^4-10n^3-20n^2-15n^1-4 \end{cases} 2g(n,1)=n1+04g(n,2)=n2+0n18g(n,3)=n3+0n23n1216g(n,4)=n4+0n36n28n1332g(n,5)=n5+0n410n320n215n14

你是否注意到了,表格中的数据不足以让我求出 g ( n , 5 ) g(n,5) g(n,5) 呢?事实上,我发现 g ( m − 1 , m ) = 0 g(m-1,m)=0 g(m1,m)=0 ,所以我就用这个点补齐了 g ( n , 5 ) g(n,5) g(n,5) 所需的 6 6 6 个点。为什么有这个零点呢?瞪眼法得到的。就是我不想打暴力程序了,所以我想用用更小的    n    \sout{\;n\;} n值。

然后我又想,根据因式定理,我们知道最高项系数,只需要知道它的所有根即可确定多项式。所以我试着将这个特殊的根去掉。
{ g ( n , 1 ) 2 n = 1 g ( n , 2 ) 4 ( n − 1 ) = n + 1 g ( n , 3 ) 8 ( n − 2 ) = n 2 + 2 n + 1 g ( n , 4 ) 16 ( n − 3 ) = n 3 + 3 n 2 + 3 n + 1 g ( n , 5 ) 32 ( n − 4 ) = n 4 + 4 n 3 + 6 n 2 + 4 n + 1 \begin{cases} {g(n,1)\over 2n}=1\\ {g(n,2)\over 4(n-1)}=n+1\\ {g(n,3)\over 8(n-2)}=n^2+2n+1\\ {g(n,4)\over 16(n-3)}=n^3+3n^2+3n+1\\ {g(n,5)\over 32(n-4)}=n^4+4n^3+6n^2+4n+1 \end{cases} 2ng(n,1)=14(n1)g(n,2)=n+18(n2)g(n,3)=n2+2n+116(n3)g(n,4)=n3+3n2+3n+132(n4)g(n,5)=n4+4n3+6n2+4n+1

握了棵草!这还真他母亲的可以!右边显然是 ( n + 1 ) m − 1 (n+1)^{m-1} (n+1)m1 啊!


以下是正文。

不难发现 如下规律:
g ( n , m ) = ( n + 1 ) m − 1 ⋅ ( n − m + 1 ) ⋅ 2 m g(n,m)=(n+1)^{m-1}\cdot(n-m+1)\cdot 2^m g(n,m)=(n+1)m1(nm+1)2m

思路

真正合理的做法请看这里。

将座位连接成一个环,并且加入 死亡座位 n + 1 n+1 n+1(在环上就会在 1 1 1 n n n 之间)。问题转化为,不能让 n + 1 n+1 n+1 号座位被使用(在此种情形下,可以认为所有人都找到了座位)。

任意选,情况数是 [ 2 ( n + 1 ) ] m \big[2(n+1)\big]^m [2(n+1)]m 。多少种情况下 n + 1 n+1 n+1 号座位没被使用呢?考虑一个座位没有被使用的 概率(本质就是方案数除以总方案数嘛)。每个座位是 等价的(这也是为什么前面的乱选方案数允许 n + 1 n+1 n+1 号座位作为心仪座位——否则座位是不等价的),所以概率就是 1 − m n + 1 1-\frac{m}{n+1} 1n+1m 嘛。所以总方案数
g ( n , m ) = 2 m ⋅ ( n + 1 ) m ⋅ ( 1 − m n + 1 ) g(n,m)=2^m\cdot (n+1)^m\cdot\left(1-\frac{m}{n+1}\right) g(n,m)=2m(n+1)m(1n+1m)

牢骚语:何人何等脑洞,敢于用此妙计?为啥要接链成环啊?为啥啊?为啥啊?

代码

暴力模拟的代码我给删掉了……这里就贴一份高斯消元的代码吧。

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 105;
double a[MaxN][MaxN]; int n;
void gauss(){
//	printf("a =\n");
//	for(int i=0; i<n; ++i,puts(""))
//		for(int j=0; j<=n; ++j)
//			printf("%.0f ",a[i][j]);
	for(int i=0; i<n; ++i){
		int row = -1;
		for(int j=i; j<n; ++j){
			if(a[j][i] < 0) // flip
			for(int k=i; k<=n; ++k)
				a[j][k] = -a[j][k];
			if(row == -1 ||
			a[j][i] > a[row][i])
				row = j;
		}
		swap(a[row],a[i]);
		for(int j=0; j<n; ++j) if(j != i)
		for(int k=n; k>=i; --k)
			a[j][k] -= a[i][k]*a[j][i]/a[i][i];
	}
//	printf("a_ =\n");
//	for(int i=0; i<n; ++i,puts(""))
//		for(int j=0; j<=n; ++j)
//			printf("%.6f ",a[i][j]);
	for(int i=0; i<n; ++i)
		printf("%.6f\n",a[i][n]/a[i][i]);
}
int totSy;
void insert(int x,int y){
	double t = 1;
	for(int i=n-1; ~i; --i)
		a[totSy][i] = t, t *= x;
	a[totSy ++][n] = y; // result
}

int main(){
	n = 6;
	insert(4,0);
	insert(5,41472);
	insert(6,153664);
	insert(7,393216);
	insert(8,839808);
	insert(9,1600000);
	gauss();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值