【題解】[AGC022F] Checkers

[AGC022F] Checkers

\(\text{Solution:}\)

看了題解半天之後的一種理解。

首先我們需要發現,一次操作本質上是選擇兩個點,把其中一個的座標乘 \(-1,\) 另一個乘 \(2.\)

那麼考慮建立一個點,然後把操作的兩個點向這個點連邊,邊權分別是 \(-1,2,\) 最後會形成一棵二叉樹。

而由於 \(x\) 遠大於 \(2^n,\) 故而各操作之間一定不會重疊(相當於若干基底),於是我們只需要考慮最終座標中每個 \(x\) 的係數。

在上述模型中,顯然一個點的係數就是它到根路徑的乘積。

考慮一個自上而下的 \(dp.\) 每次考慮 \(dp\) 到當前層,有多少 “葉子” (也就是所有非當前最高次冪的節點,因爲它們一定會停留不再分裂),以及當前最高次冪中,係數爲正爲負的分別有多少個。

\(f_{i,x,y}\) 爲符合上述定義的形態,不計順序的方案數。

注意到我們轉移只需要用到正負的數目,所以至於最高次冪是 \(2\) 的幾次冪我們不關心。

首先容易知道到根處的狀態應該是 \(f_{0,1,0}\) 因爲當它父親邊係數是 \(1.\)

考慮從後往前轉移,首先容易有 \(f[n][0][0]=1\)

考慮枚舉每次分裂 \(a\) 個正的次冪,\(b\) 個負的次冪,那麼對應的葉子應當爲 \(i+x+y\) 個,同時新產生的最高次冪正的和負的分別就是 \(a,b.\)

考慮新產生的葉子。容易發現產生的時候同時也會新生成另一個,具體而言,\(2^k\to -2^k,2^{k+1},-2^k\to 2^{k},-2^{k+1}\)

於是我們可以發現,設原來最高次冪是 \(2^k,\) 更新之後就是 \(2^{k+1},\) 而剩下的葉子中新出現的,有 \(x-a+b\) 個是正的,有 \(y-b+a\) 個是負的。

考慮如何計算答案。如果我們知道最終座標裏面每個 \(2^k,-2^k\) 的個數,那麼這個就應當是一個多重組合數。所以此時我們需要把兩類葉子的階乘除掉,也就是 \((x-a+b)!,(y-b+a)!\)

#include <bits/stdc++.h>
using namespace std;
typedef double db;
#define int long long
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
#define bc __builtin_popcount
#define pc putchar
#define ci const int&
const int mod = 1e9 + 7;
const db eps = 1e-10;
const int inf = (1 << 30);
inline int Max(ci x, ci y) {return x > y ? x : y;}
inline int Min(ci x, ci y) {return x < y ? x : y;}
inline db Max(db x, db y) {return x - y > eps ? x : y;}
inline db Min(db x, db y) {return x - y < eps ? x : y;}
inline int Add(ci x, ci y, ci M = mod) {return (x + y) % M;}
inline int Mul(ci x, ci y, ci M = mod) {return 1ll * x * y % M;}
inline int Dec(ci x, ci y, ci M = mod) {return (x - y + M) % M;}
typedef pair<int, int> pii;
inline int Abs(int x) {return x < 0 ? -x : x;}
//char buf[1<<21],*p1=buf,*p2=buf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char Obuf[105000],*O=Obuf;//Siz shoule be the size of Out File
int pst[30],ptop;
inline void Fprint(){fwrite(Obuf,1,O-Obuf,stdout);}
inline void Fwrite(int x){
  if(x==0){*O++='0';return;}
  if(x<0)*O++='-',x=-x;ptop=0;
  while(x)pst[++ptop]=x%10,x/=10;
  while(ptop)*O++=pst[ptop--]+'0';
  if(O-Obuf>100000)Fprint(),O=Obuf;
}
inline int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') w = -1;ch = getchar();}
    while (isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
    return s * w;
}
inline void write(int x) {
    if (x < 0)putchar('-'), x = -x;
    if (x > 9)write(x / 10);
	pc(x % 10 + '0');
}
inline int qpow(int x, int y) {
    int res = 1;
    while (y) {if (y & 1)res = Mul(res, x);x = Mul(x, x);y >>= 1;}
    return res;
}
inline void cadd(int &x, int y) {x += y;}
inline void cmul(int &x, int y) {x *= y;}
inline void cmax(int &x, int y) {x = Max(x, y);}
inline void cmin(int &x, int y) {x = Min(x, y);}
const int N = 2e5 + 10;
namespace Refined_heart{
	int n,fac[N],inv[N];
	int f[51][51][51];
	void solve(){
		n=read();
		fac[0]=inv[0]=1;
		for(int i=1;i<=500;++i)fac[i]=Mul(fac[i-1],i);
		for(int i=1;i<=500;++i)inv[i]=qpow(fac[i],mod-2);
		f[n][0][0]=1;
		for(int i=n-1;i>=0;--i){
			for(int x=0;x<=n-i;++x)
				for(int y=0;y<=n-i-x;++y){
//					printf("{%d %d %d}\n",i,x,y);
					int R=1;
					for(int a=0;a<=n-i-x-y;++a)
						for(int b=0;b<=n-i-x-y-a;++b)
							if(x-a+b>=0&&y+a-b>=0)
								f[i][x][y]=Add(f[i][x][y],Mul(f[i+x+y][a][b],Mul(inv[x-a+b],inv[y-b+a])));//,printf("[%d %d]\n",x-a+b,y-b+a),R=Mul(R,Mul(inv[x-a+b],inv[y-b+a]));
//					printf("%d:------\n",qpow(R,mod-2));
//					puts("--------------------------");
				}
					
		}
		printf("%d\n",qpow(f[0][1][0],1));
		printf("%d\n",Add(f[1][1][0],f[1][0][1]));
		write(Mul(f[0][1][0],fac[n]));
		
	}
}
signed main(){
	Refined_heart::solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章