【Petrozavodsk WC 2018d2: ITMO U 1 Contest E】Enumeration of Tournaments 題解

題目大意

  有 nn 個人玩淘汰賽。
  每一輪,假設當前還剩 kk 人,則他們隨機分成 k2\lfloor \frac k2 \rfloor 組(kk 爲奇數時有一人輪空),最後晉級 k2\lceil \frac k2 \rceil 人。每個人能力互不相同,兩人對打時一定是能力強者獲勝。
  求所有可能的局面數,答案對 2642^{64} 取模。

  1n10181 \leq n \leq 10^{18}

  注意題面坑:Two tournaments are called different if there is a game (between two participants) in one of the tournaments that doesn’t occur in the other tournament. 這句話是錯的!

\\
\\
\\

題解

  這題的難點在於揣摩出題人的正確題意
  題意演我 3 個小時出題人今晚買菜必漲價

  考慮遞推。設 f(n)f(n)nn 個人時的方案數。
  當 nn 爲奇數時,設 m=n2m=\lfloor \frac n2 \rfloor,輪空一個人有 nn 種情況,分組有 (2m)!2mm!\frac{(2m)!}{2^m m!} 種情況(給 2m2m 個人標號 11~2m2miim+im+i 一組,然後去重),晉級的 m+1m+1 個人打接下來的比賽有 f(m+1)f(m+1) 種情況,故
f(n)=n(2m)!2mm!f(m+1)=n!!f(n2)f(n)=n\cdot \frac{(2m)!}{2^m m!} \cdot f(m+1)=n!!\cdot f(\lceil \frac n2 \rceil)

  偶數同理,得到
f(n)=(2m)!2mm!f(m)=(n1)!!f(n2)f(n)=\frac{(2m)!}{2^m m!} \cdot f(m)=(n-1)!!\cdot f(\frac n2)
  如何求 n!! mod 264n!!~\text{mod}~2^{64} 呢?這個技巧還是不錯的。
  首先這個 nn 是個奇數,設 Pk(x)=i=1k(2x+2i1)=(2x+1)(2x+3)(2x+2k1)P_k(x)=\prod_{i=1}^k(2x+2i-1)=(2x+1)(2x+3)\cdots(2x+2k-1),而這個 Pk(0)P_k(0) 就是要求的值。
  Pk(x)P_k(x) 是關於 xx 的多項式,而這個多項式對於 x64x^{64} 及以上的項,係數都含有 2642^{64},模意義下爲 00,因此這個多項式只用考慮前 64 項(x0x^0x63x^{63})。因此這個多項式的普通乘法是 O(6464)O(64\cdot 64) 的。
  考慮倍增求這個多項式,假設已知 Pk(x)P_k(x),那麼 P2k(x)=Pk(x)Pk(x+k)P_{2k}(x)=P_k(x)P_k(x+k),具體實現就先用 O(6464)O(64\cdot 64) 的時間求出 Pk(x+k)P_k(x+k),然後再用 O(6464)O(64\cdot 64) 的乘法求出 P2k(x)P_{2k}(x)。通過 P2k(x)P_{2k}(x)P2k+1(x)P_{2k+1}(x) 同理。

題面的題意

  如果按照題面的題意要怎麼做呢?

  其實差別就是,比如 n=3n=3 的時候,3 先跟 1 打再跟 2 打,與 3 先跟 2 打再跟 1 打是等價的。
  我們給要對打的兩個人連邊,它形成了一棵樹,所以不同的局面數就是合法的生成樹的個數。
  有標號生成樹計數,考慮 prufer 序。

  然後不會了

代碼

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

const int maxw=130;

struct P{
	ULL a[maxw];
	LL k;
} zero,re;

LL n;

ULL C[70][70];
void C_Pre(int n)
{
	fo(i,0,n)
	{
		C[i][0]=1;
		fo(j,1,i) C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
}

P mul1(const P &x,P y)
{
	fo(i,0,63)
	{
		ULL kpow=x.k;
		for(int j=i-1; j>=0; j--, kpow*=x.k) y.a[j]+=C[i][i-j]*kpow*y.a[i];
	}
	re.k=x.k+y.k;
	fo(i,0,63) re.a[i]=0;
	fo(i,0,63)
		fo(j,0,63) re.a[i+j]+=x.a[i]*y.a[j];
	return re;
}
P mul2(const P &x,const P &y)
{
	fo(i,0,63) re.a[i]=0;
	fo(i,0,63)
		fo(j,0,63) re.a[i+j]+=x.a[i]*y.a[j];
	return re;
}

P fac(LL n)
{
	P re=zero, x=zero;
	re.a[0]=1, x.k=1, x.a[0]=1, x.a[1]=2;
	for(n=(n+1)>>1; n; n>>=1, x=mul1(x,x)) if (n&1) re=mul1(re,x);
	return re;
}

P dfs(LL n)
{
	if (n<=2)
	{
		re=zero;
		re.a[0]=1;
		return re;
	}
	return (n&1) ?mul2(dfs(n-(n>>1)),fac(n)) :mul2(dfs(n>>1),fac(n-1)) ;
}

int main()
{
	C_Pre(65);
	
	scanf("%lld",&n);
	
	P ans=dfs(n);
	printf("%llu\n",ans.a[0]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章