1. 程式人生 > 實用技巧 >題解 2020.10.24 考試 T3 數列

題解 2020.10.24 考試 T3 數列

題目傳送門

題目大意

給出一個數 \(n\),你要構造一個數列,滿足裡面每個數都是 \(n\) 的因子,且每一個數與前面不互質的個數不超過 \(1\)。問有多少種合法方案。

保證 \(n\) 的不同質因子個數 \(\le 6\)

思路

這個題不是很難,只是比較難寫。不過 \(\Theta(6\times 3^6)\) 的做法感覺比較有意思,但是我寫的是玄學時間複雜度的做法。

我們可以看出數列長度最大也就 \(12\),而且質因子個數也很少,不難想到狀壓 dp,我們發現這個狀壓 dp 完全沒有什麼難點,直接 \(f_{S,x}\) 表示當前狀態為 \(S\),已經填了 \(x\) 位的方案數。對於一個狀態,肯定是每個質因子的狀態組合起來,你發現重要的不過就是這個質因子出現了幾次,如果只出現一次是哪些質因子一起出現的。你把這個壓進狀態就好了。

似乎預處理了之後轉移可以更快,但是懶得了。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define mod 1000000007
#define ll long long

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

ll N;
int cnt,yz[15],pw[15];

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
void Mul (int &a,int b){a = mul (a,b);}
void Dec (int &a,int b){a = dec (a,b);}
void Add (int &a,int b){a = add (a,b);}

void Makeyz (ll n){
	for (Int i = 2;1ll * i * i <= n;++ i){
		if (n % i == 0){
			yz[++ cnt] = i;
			while (n % i == 0) n /= i,pw[cnt] ++;
		}
	}
	if (n > 1) yz[++ cnt] = n,pw[cnt] = 1;
} 

int f[1 << 19][15];

signed main(){	
	read (N),Makeyz (N);
	f[0][0] = 1;int ans = 0;
	for (Int x = 0;x < cnt * 2;++ x){	
		for (Int nowS = 0;nowS < (1 << cnt * 3);++ nowS){
			if (!f[nowS][x]) continue;
			vector <int> unused,cho;
			bool used[12] = {};
			for (Int i = 1;i <= cnt;++ i){
				int k = nowS >> (i - 1) * 3 & 7;
				if (!k) unused.push_back (i);
				else if (k < 7 && !used[k]) cho.push_back (i),used[k] = 1;
			}
			int siz1 = unused.size(),siz2 = cho.size();
			for (Int S = 0;S < (1 << siz1);++ S){
				int kase = 1,S1 = 0,fir = 0;
				for (Int i = 0;i < siz1;++ i) if (S >> i & 1) 
					fir = fir ? fir : unused[i],Mul (kase,pw[unused[i]]),S1 |= (1 << (unused[i] - 1) * 3) * fir;
				if (S) Add (f[nowS | S1][x + 1],mul (kase,f[nowS][x]));
				if (!cho.size()) continue;
				for (Int u : cho){
					int k = nowS >> (u - 1) * 3 & 7,siz2 = 0;
					vector <int> uni;
					for (Int i = 1;i <= cnt;++ i) if ((nowS >> (i - 1) * 3 & 7) == k) uni.push_back (i);
					siz2 = uni.size();
					for (Int choS = 1;choS < (1 << siz2);++ choS){
						int newS = nowS | S1,fir = 0x7f7f7f7f,kase1 = 1;
						for (Int i = 0;i < siz2;++ i) 
							if (choS >> i & 1) newS -= (1 << (uni[i] - 1) * 3) * (k - 7),Mul (kase1,pw[uni[i]]);
							else fir = min (fir,uni[i]);
						for (Int i = 0;i < siz2;++ i) if (!(choS >> i & 1)) newS -= (1 << (uni[i] - 1) * 3) * (k - fir);
						Add (f[newS][x + 1],mul (f[nowS][x],mul (kase,kase1)));
					}
				}
			}
		}
	}
	for (Int x = 1;x <= cnt * 2;++ x) for (Int nowS = 0;nowS < (1 << cnt * 3);++ nowS) Add (ans,f[nowS][x]);
	write (ans),putchar ('\n');
	return 0;
}