「THUSCH 2017」如果奇迹有颜色(burnside引理+状压dp+打表+BM+常系数齐次线性递推)

https://loj.ac/problem/2981

这真TM是个防AK题。

考虑先套上burnside,枚举置换\(i\),发现问题变成\(gcd(i,n)\)长的环,染m种颜色,连续m个不会出现m个颜色的方案数,记\(f(d)\)表示长为d的环的方案数,则\(Ans=\sum_{d|n}f(d)*\phi(n/d)\)

现在就是怎么求一个\(f(n)\)的问题了。

暴力:

状态压缩dp,记\(dp[i][S]\)表示确定了前\(i\)个,第\(i-m+2\)\(i\)个颜色状态是\(S\),的方案数。

至于环的限制,只要\(n\)步之后\(S\)等于一开始的\(S\)就好了。

用矩阵乘法优化,时间复杂度:\((m^{m-1})^3*log~n\)

可以通过\(m<=4\)的数据获得55分。

BM:

\(m\)确定时,\(f[n]\)肯定是一个有递推式的序列,且由上一个dp知道递推式长度\(\le m^{m-1}\)

这样就可以BM后常系数齐次线性递推。

那么问题就是怎么快速算\(f[1..n]\)

考虑不用矩阵乘法,直接dp:
先枚举一个\(w\),表示前\(w\)个的颜色是\(1,2,3,...,w\),第\(w+1\)个颜色\(\in[1,w]\)

然后用刚才那个\(dp[i][S]\),在\(n-w\)步后,看看\(S\)和前w个是否冲突,不冲突,则\(f[n]+=dp[n-w][S]*P_m^w\)

这样算\(f[1..n]\)的复杂度是\(O(n*(m^{m-1})*m^2)\)\(m=7,n=1000\)大概要算20s,本地算出,打表即可。

接着跑BM,实际上,当\(m=7\)时,递推式长度也才\(410\)

这样就不用多项式取模,直接暴力取模的常系数齐次线性递推即可。

代码(打表后的BM和递推):https://loj.ac/submission/782204

代码(打表程序):

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 1e9 + 7;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const int M = 117649;

int n, m, t, am[8];


int bz[M][8], to[M][8];

namespace sub1 {
	int cx[10];
	void build1() {
		ff(i, 0, t)	{
			ff(j, 0, m) {
				ff(k, 0, m) cx[k] = 0; cx[j] = 1;
				ff(k, 0, m - 1) cx[i / am[k] % m] = 1;
				int ky = 0;
				ff(k, 0, m) if(!cx[k]) ky = 1;
				bz[i][j] = ky;
				if(ky) to[i][j] = (i / m) + j * am[m - 2];
			}
		}
	}
}
	

int po[M][8];

namespace sub2 {
	int c[20], cx[10];
	int pd(int n) {
		fo(i, 1, n - m + 1) {
			fo(j, 0, m - 1) cx[j] = 0;
			fo(j, 0, m - 1) cx[c[i + j]] = 1;
			int ky = 0;
			fo(j ,0, m - 1) if(!cx[j]) ky = 1;
			if(!ky) return 0;
		}
		return 1;
	}
	void build2() {
		ff(s, 0, am[m - 1]) {
			fo(i, 1, m - 1) c[i] = s / am[i - 1] % m;
			fo(w, 1, m - 1) {
				fo(j, m, m + w - 1) c[j] = j - m;;
				po[s][w] = pd(m + w - 1);
			}
		}
	}
}

ll fac[10], nf[10];
ll P(int n, int m) { return fac[n] * nf[n - m] % mo;}

ll a[2005], f[2][M], o;

int main() {
	freopen("a.out", "w", stdout);
	for(m = 0; m <= 7; m ++) {
		fo(i, 0, 1000) a[i] = 0;
		am[0] = 1; fo(i, 1, m) am[i] = am[i - 1] * m;
		t = am[m - 1];
		sub1 :: build1();
		sub2 :: build2();
		fo(i, 0, m - 1) a[i] = ksm(m, i);
		fac[0] = 1; fo(i, 1, m) fac[i] = fac[i - 1] * i;
		fo(i, 0, m) nf[i] = ksm(fac[i], mo - 2);
		fo(w, 1, m - 1) {
			memset(f, 0, sizeof f);
			int st = 0;
			fo(i, 0, w - 1) st += am[(m - 1 - w) + i] * i;
			f[o][st] = 1;
			fo(p, w + 1, 1000) {
				memset(f[!o], 0, sizeof f[!o]);
				ff(i, 0, t) if(f[o][i]) {
					fo(j, 0, (p == w + 1 ? w - 1 : m - 1)) if(bz[i][j]) {
						f[!o][to[i][j]] = (f[!o][to[i][j]] + f[o][i]) % mo;
					}
				}
				o = !o;
				if(p >= m) {
					ff(i, 0, t) if(po[i][w]) {
						a[p] = (a[p] + P(m, w) * f[o][i]) % mo;
					}
				}
			}
		}
		pp("{");
		fo(i, 0, 1000) {
			pp("%lld", a[i]);
			if(i != 1000) pp(",");
		}
		pp("},");
		hh;
//		fo(i, 1, 100) pp("%lld ", a[i]);
	}
	
}
posted @ 2020-04-06 21:20  Cold_Chair  阅读(388)  评论(0编辑  收藏  举报