【2019 江苏省队集训】Day2 解题报告

这篇博客详细介绍了2019年江苏省队集训第二天的解题报告,涉及到的问题包括瓷砖排列、分块处理动态规划优化以及灵力移动问题。通过 Burnside引理 和动态规划优化,解决瓷砖排列问题;利用分块技术优化数据结构,提高处理效率;最后通过数学分析和转化,解决灵力移动问题,并给出权值和的计算方法。

【T1】 朝夕相处

【思路要点】

  • 记不考虑旋转同构的答案为 G ( N ) G(N) G(N),答案为 F ( N ) F(N) F(N),由 B u r n s i d e Burnside Burnside引理,有 F ( N ) = ∑ i ∣ N G ( i ) φ ( N i ) N F(N)=\frac{\sum_{i\mid N}G(i)\varphi(\frac{N}{i})}{N} F(N)=NiNG(i)φ(iN)
  • 剩余问题在于计算 G ( N ) G(N) G(N) ,考虑状态 ( i , C o l a , C o l b , D i s t a , D i s t b ) (i,Cola,Colb,Dista,Distb) (i,Cola,Colb,Dista,Distb) ,表示完成左端点在 i i i 之前的所有瓷砖的放置与染色后,第一行最后一个瓷砖颜色为 C o l a Cola Cola ,位置为 i + D i s t a i+Dista i+Dista ,第二行最后一个瓷砖颜色为 C o l b Colb Colb ,位置为 i + D i s t b i+Distb i+Distb 。枚举左端点在 0 0 0 处时的状态,计算转移到 N N N 处的相同状态的方案数,对于所有状态求和,即为 G ( N ) G(N) G(N)
  • 使用矩阵乘法或 B e r l e k a m p − M a s s e y Berlekamp-Massey BerlekampMassey 算法可以对该动态规划进行优化。
  • 状态总数为 O ( K 2 ) O(K^2) O(K2),若采用 B e r l e k a m p − M a s s e y Berlekamp-Massey BerlekampMassey 算法优化动态规划,总时间复杂度为 O ( M K 6 L o g K + M 2 N L o g N ) O(MK^6LogK+M^2\sqrt{N}LogN) O(MK6LogK+M2N LogN) ,其中 M M M 为递推阶数,有 M = 31 M=31 M=31
  • 用最小表示法表示一个状态的颜色可将状态总数降至 O ( M ) O(M) O(M),总时间复杂度降为 O ( M 4 L o g M + M 2 N L o g N ) O(M^4LogM+M^2\sqrt{N}LogN) O(M4LogM+M2N LogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {
    
    x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {
    
    x = min(x, y); } 
template <typename T> void read(T &x) {
    
    
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
    
    
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
    
    
	write(x);
	puts("");
}
int power(int x, int y) {
    
    
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
void update(int &x, int y) {
    
    
	x += y;
	if (x >= P) x -= P;
}
namespace LinearSequence {
    
    
	const int MAXN = 5005;
	const int MAXLOG = 62;
	const int P = 1e9 + 7;
	vector <int> a[MAXN];
	int cnt, delta[MAXN], fail[MAXN];
	int k, h[MAXN], now[MAXLOG][MAXN];
	int power(int x, int y) {
    
    
		if (y == 0) return 1;
		int tmp = power(x, y / 2);
		if (y % 2 == 0) return 1ll * tmp * tmp % P;
		else return 1ll * tmp * tmp % P * x % P;
	}
	void times(int *res, int *x, int *y) {
    
    
		static int tmp[MAXN];
		memset(tmp, 0, sizeof(tmp));
		for (int i = 0; i <= k - 1; i++)
		for (int j = 0; j <= k - 1; j++)
			tmp[i + j] = (tmp[i + j] + 1ll * x[i] * y[j]) % P;
		for (int i = 2 * k - 2; i >= k; i--) {
    
    
			int val = tmp[i]; tmp[i] = 0;
			for (unsigned j = 0; j < a[cnt].size(); j++)
				tmp[i - j - 1] = (tmp[i - j - 1] + 1ll * val * a[cnt][j]) % P;
		}
		memcpy(res, tmp, sizeof(tmp));
	}
	void init(int n, int *val) {
    
    
		for (int i = 0; i <= cnt; i++)
			a[i].clear();
		cnt = 0;
		for (int i = 1; i <= n; i++) {
    
    
			delta[i] = val[i];
			for (unsigned j = 0; j < a[cnt].size(); j++)
				delta[i] = (delta[i] - 1ll * a[cnt][j] * val[i - j - 1] % P + P) % P;
			if (delta[i] == 0) continue;
			fail[cnt] = i;
			if (cnt == 0) {
    
    
				a[++cnt].resize(i);
				continue;
			}
			int mul = 1ll * delta[i] * power(delta[fail[cnt - 1]], P - 2) % P;
			a[cnt + 1].resize(i - fail[cnt - 1] - 1);
			a[cnt + 1].push_back(mul);
			for (unsigned j = 0; j < a[cnt - 1].size(); j++)
				a[cnt + 1].push_back(1ll * a[cnt - 1][j] * (P - mul) % P);
			if (a[cnt + 1].size() < a[cnt].size()) a[cnt + 1].resize(a[cnt].size());
			for (unsigned j = 0; j < a[cnt].size(); j++)
				a[cnt + 1][j] = (a[cnt + 1][j] + a[cnt][j]) % P;
			cnt++;
		}
		assert(n >= a[cnt].size() * 2 + 5);
		memset(now, 0, sizeof(now));
		k = a[cnt].size();
		if (k == 1) now[0][0] = a[cnt][0];
		else now[0][1] = 1;
		for (int i = 1; i <= 2 * k; i++)
			h[i] = val[i];
		for (int p = 1; p < MAXLOG; p++)
			times(now[p], now[p - 1], now[p - 1]);
	}
	int query(long long n) {
    
    
		if (n <= k) return h[n]; n -= k;
		static int res[MAXN];
		memset(res, 0, sizeof(res));
		res[0] = 1;
		for (int p = 0; p < MAXLOG; p++) {
    
    
			long long tmp = 1ll << p;
			if (n & tmp) times(res, res, now[p]);
		}
		int ans = 0;
		for (int i = 0; i <= k - 1; i++)
			ans = (ans + 1ll * res[i] * h[i + k]) % P;
		return ans;
	}
}
struct State {
    
    int cola, colb, dista, distb; };
bool operator < (State a, State b) {
    
    
	if (a.cola == b.cola) {
    
    
		if (a.colb == b.colb) {
    
    
			if (a.dista == b.dista) return a.distb < b.distb;
			else return a.dista < b.dista;
		} return a.colb < b.colb;
	} return a.cola < b.cola;
}
int n, m, ans[MAXN];
set <pair <State, int>> start;
map <State, int> mp[MAXN];
void initstates() {
    
    
	State now = (State) {
    
    0, 0, 0, 0}, res = now;
	res.cola = res.colb = 1;
	start.emplace(res, m);
	for (int dj = 1; dj <= 3; dj++)
	for (int dk = 1; dk <= 3; dk++) {
    
    
		State res = now;
		res.dista = dj, res.distb = dk
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值