題目大意
有 個人玩淘汰賽。
每一輪,假設當前還剩 人,則他們隨機分成 組( 爲奇數時有一人輪空),最後晉級 人。每個人能力互不相同,兩人對打時一定是能力強者獲勝。
求所有可能的局面數,答案對 取模。
注意題面坑: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 個小時出題人今晚買菜必漲價
考慮遞推。設 爲 個人時的方案數。
當 爲奇數時,設 ,輪空一個人有 種情況,分組有 種情況(給 個人標號 ~, 和 一組,然後去重),晉級的 個人打接下來的比賽有 種情況,故
偶數同理,得到
如何求 呢?這個技巧還是不錯的。
首先這個 是個奇數,設 ,而這個 就是要求的值。
是關於 的多項式,而這個多項式對於 及以上的項,係數都含有 ,模意義下爲 ,因此這個多項式只用考慮前 64 項( 至 )。因此這個多項式的普通乘法是 的。
考慮倍增求這個多項式,假設已知 ,那麼 ,具體實現就先用 的時間求出 ,然後再用 的乘法求出 。通過 求 同理。
題面的題意
如果按照題面的題意要怎麼做呢?
其實差別就是,比如 的時候,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]);
}