【CodeChef】October Challenge 2018 (Div. 1 + Div. 2) 题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82973997

【比赛链接】

**【BBRICKS】**Beautiful Bricks

【思路要点】

  • 上下两个砖块中,至多有一个黑色。
  • 连续的一段存在黑色的行共有两种放置的方案。
  • 枚举有几段连续的存在黑色的行,用组合数计算答案。
  • 单组数据时间复杂度 O ( K ) O(K)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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 n, k, bit[MAXN], fac[MAXN], inv[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;
}
int getc(int x, int y) {
	if (y > x) return 0;
	else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P;
}
int main() {
	k = 1e3;
	fac[0] = 1;
	for (int i = 1; i <= k; i++)
		fac[i] = 1ll * fac[i - 1] * i % P;
	inv[k] = power(fac[k], P - 2);
	for (int i = k - 1; i >= 0; i--)
		inv[i] = inv[i + 1] * (i + 1ll) % P;
	bit[0] = 1;
	for (int i = 1; i <= k; i++)
		bit[i] = bit[i - 1] * 2 % P;
	int T; read(T);
	while (T--) {
		read(n), read(k);
		int tmp = n - k + 1;
		int ans = 0, now = 1;
		if (tmp < 0) now = 0;
		for (int i = 1; i <= k; i++) {
			now = 1ll * now * (tmp - i + 1) % P;
			ans = (ans + 1ll * getc(k - 1, i - 1) * now % P * inv[i] % P * bit[i]) % P;
		}
		writeln(ans);
	}
	return 0;
}

**【BITOBYT】**Byte to Bit

【思路要点】

  • 每个时刻只可能存在一种角色,每 26 26 个时间单位数量翻倍。
  • 直接计算答案即可。
  • 时间复杂度 O ( T L o g N ) O(TLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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("");
}
ll power(int x, int y) {
	if (y == 0) return 1;
	ll tmp = power(x, y / 2);
	if (y % 2 == 0) return tmp * tmp;
	else return tmp * tmp * x;
}
int main() {
	int T; read(T);
	while (T--) {
		int n; read(n);
		int q = n / 26, r = n % 26;
		if (r == 0) q--, r = 26;
		ll x = 0, y = 0, z = 0, ans = power(2, q);
		if (r <= 2) x = ans;
		else if (r <= 10) y = ans;
		else z = ans;
		printf("%lld %lld %lld\n", x, y, z);
	}
	return 0;
}

**【CCIRCLES】**Chef and Circles

【思路要点】

  • 枚举一对圆,能够达到的距离是一个区间,算出该区间即可。
  • 注意圆相互包含和外离的情况。
  • 时间复杂度 O ( K + N 2 ) O(K+N^2)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 5;
const int MAXV = 1e6 + 5;
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 n, q, r[MAXN], ans[MAXV];
struct point {int x, y; } a[MAXN];
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
long long dist(point a) {return 1ll * a.x * a.x + 1ll * a.y * a.y; }
int main() {
	read(n), read(q);
	for (int i = 1; i <= n; i++)
		read(a[i].x), read(a[i].y), read(r[i]);
	for (int i = 1; i <= n; i++)
	for (int j = i + 1; j <= n; j++) {
		ll tmp = dist(a[i] - a[j]);
		int Max = sqrt(tmp);
		int Min = sqrt(tmp);
		if (1ll * Min * Min < tmp) Min++;
		if (Min - r[i] - r[j] >= 0) Min = Min - r[i] - r[j];
		else Min = max(0, abs(r[i] - r[j]) - Max);
		Max = min(1000000, Max + r[i] + r[j]);
		ans[Min]++, ans[Max + 1]--; 
	}
	for (int i = 1; i < MAXV; i++)
		ans[i] += ans[i - 1];
	while (q--) {
		int x; read(x);
		printf("%d\n", ans[x]);
	}
	return 0;
}

**【CHSERVE】**Chef and Serves

【思路要点】

  • 直接计算答案就好。
  • 时间复杂度 O ( T ) O(T)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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 main() {
	int T; read(T);
	while (T--) {
		int x, y, k;
		read(x), read(y), read(k);
		int tmp = (x + y + 1) % (2 * k);
		if (tmp == 0) tmp = 2 * k;
		if (tmp <= k) printf("CHEF\n");
		else printf("COOK\n");
	}
	return 0;
}

**【CPCOMP】**Coprime Components

【思路要点】

  • 首先显然所有形如 x a y b . . . z c x^ay^b...z^c 的数等价于 x y . . . z xy...z ,不妨先做这一步转化。
  • 有一种较为简单的 O ( N 2 L o g L o g V w ) O(\frac{N^2*LogLogV}{w}) 的做法,用 b i t s e t bitset 记录每一个质数作为质因数出现的位置,对于一个数 x x 将其所有质因数的 b i t s e t bitset 或起来再取反,就可以得到它的边集,简单搜索即可。
  • 但这样的做法复杂度太高,注意到 &gt; V &gt;\sqrt{V} 的质因数每个数只有一个,考虑将所有数按照最大的质因数分组。若 x x 最大的质因数 p &gt; V p&gt;\sqrt{V} ,那么 x x 会连向不是同组、且与 x p \frac{x}{p} 互质的数,通过一些细节处理可以在 O ( N V ) O(N\sqrt{V}) 的时间复杂度内进行等价的连边。
  • 剩余的不存在 &gt; V &gt;\sqrt{V} 的质因数的数的个数不超过 3 1 0 4 3*10^4
  • 时间复杂度 O ( N V + M 2 L o g L o g V w ) O(N\sqrt{V}+\frac{M^2*LogLogV}{w}) ,其中 M 3 1 0 4 M\leq 3*10^4

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int P = 100;
const int Q = 3e4;
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 tot, prime[MAXN], miu[MAXN];
int f[MAXN], g[MAXN], realn[MAXN], num[MAXN];
void init(int n) {
	realn[1] = miu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) {
			prime[++tot] = f[i] = realn[i] = i;
			num[i] = tot;
			miu[i] = -1;
		}
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			if (prime[j] == f[i]) realn[tmp] = realn[i], miu[tmp] = 0;
			else realn[tmp] = realn[i] * prime[j], miu[tmp] = -miu[i];
			f[tmp] = prime[j];
		}
	}
	for (int i = 2; i <= n; i++) {
		int tmp = i;
		while (f[tmp] != tmp) tmp /= f[tmp];
		g[i] = tmp;
	}
}
int fa[MAXN];
int par(int x) {
	while (x != fa[x])
		x = fa[x] = fa[fa[x]];
	return x;
}
int main() {
	int n; read(n);
	vector <int> a(n + 1);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	sort(a.begin(), a.end());
	if (a[1] == 1) {
		printf("%d\n", 1);
		return 0;
	}
	int Max = a.back();
	init(Max);
	for (int i = 1; i <= n; i++)
		a[i] = realn[a[i]];
	sort(a.begin(), a.end());
	Max = a.back();
	static vector <int> factors[MAXN];
	for (int i = 1; i <= Max; i++)
	for (int j = i; j <= Max; j += i)
		factors[j].push_back(i);
	static int cnt[MAXN];
	for (int i = 1; i <= n; i++)
	for (auto j : factors[a[i]])
		cnt[j]++;
	int ans = 0;
	vector <int> b;
	for (int i = 1; i <= n; i++) {
		int tmp = 0;
		for (auto j : factors[a[i]])
			tmp += cnt[j] * miu[j];
		if (tmp == 0) ans++;
		else b.push_back(a[i]);
	}
	b.erase(unique(b.begin(), b.end()), b.end());
	a = b, n = a.size();
	int oldn = n;
	for (int i = 0; i < n; i++)
		fa[i] = i;
	int limit = sqrt(a.back());
	if (limit * limit <= a.back()) limit++;
	static int gcd[448][448];
	for (int i = 0; i < limit; i++)
	for (int j = 0; j < limit; j++)
		gcd[i][j] = __gcd(i, j);
	sort(a.begin(), a.end(), [&] (int x, int y) {return g[x] < g[y]; });
	static deque <pair <int, int> > p[MAXN];
	vector <int> used;
	for (int i = 0; i < n; i++)
		if (g[a[i]] >= limit) {
			p[a[i] / g[a[i]]].push_back(make_pair(g[a[i]], i));
			if (p[a[i] / g[a[i]]].size() == 1) used.push_back(a[i] / g[a[i]]);
		}
	for (int i = 0; i < n; i++)
	for (auto j : used)
		if (gcd[a[i] % j][j] == 1) {
			pair <int, int> tmp = make_pair(0, -1);
			while (!p[j].empty() && p[j].front().first != g[a[i]]) {
				tmp = p[j].front();
				fa[par(tmp.second)] = par(i);
				p[j].pop_front();
			}
			while (!p[j].empty() && p[j].back().first != g[a[i]]) {
				tmp = p[j].back();
				fa[par(tmp.second)] = par(i);
				p[j].pop_back();
			}
			if (tmp.second != -1) p[j].push_back(tmp);
		}
	while (!a.empty() && g[a.back()] >= limit) a.pop_back();
	n = a.size(); static bitset <Q> edge[P], vis;
	for (int i = 0; i < n; i++) {
		vis.set(i);
		int tmp = a[i];
		while (tmp != 1) {
			edge[num[f[tmp]]].set(i);
			tmp /= f[tmp];
		}
	}
	for (int i = 0; i < n; i++)
		if (vis[i]) {
			int l = 0, r = 0;
			static int q[MAXN];
			q[0] = i, vis[i] = false;
			while (l <= r) {
				int pos = q[l++];
				bitset <Q> e = vis;
				int tmp = a[pos];
				while (tmp != 1) {
					e &= ~edge[num[f[tmp]]];
					tmp /= f[tmp];
				}
				for (unsigned j = e._Find_first(); j < e.size(); j = e._Find_next(j)) {
					q[++r] = j, vis[j] = false;
					fa[par(pos)] = par(j);
				}
			}
		}
	for (int i = 0; i < oldn; i++)
		ans += par(i) == i;
	printf("%d\n", ans);
	return 0;
}

**【DISTRING】**Distinct Rows in Submatrices

【思路要点】

  • 枚举子矩形的左边界,将 N N 行进行后缀排序,令结果为 p 1 , p 2 , . . . , p N p_1,p_2,...,p_N
  • 从右向左依次考虑所有矩形的右边界,初始时,我们认为全部的 N N 行是不同的,此时的贡献可以直接由组合数算出。每当右边界达到了某两个相邻的 p i , p i + 1 p_i,p_{i+1} l c p lcp 时,我们合并 p i p_i p i + 1 p_{i+1} ,重新计算此时的贡献,我们需要减去包含与 p i p_i 相同的位置,不包含与 p i + 1 p_{i+1} 相同的位置 或是 不包含与 p i p_i 相同的位置,包含与 p i + 1 p_{i+1} 相同的位置的区间个数。
  • 用并查集 + + s t d : : s e t std::set 配合启发式合并可以完成上述功能。
  • 时间复杂度 O ( N M L o g N M + N M L o g 2 N ) O(NMLogNM+NMLog^2N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 5;
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("");
}
namespace SuffixArray {
	const int MAXN = 5e5 + 5;
	const int MAXLOG = 22;
	const int MAXC = 5e5; 
	int sa[MAXN], rnk[MAXN], height[MAXN];
	int Min[MAXN][MAXLOG], bit[MAXN], N;
	void init(int *a, int n) {
		N = n;
		static int x[MAXN], y[MAXN], cnt[MAXN], rk[MAXN];
		memset(cnt, 0, sizeof(cnt));
		for (int i = 1; i <= n; i++)
			cnt[a[i]]++;
		for (int i = 1; i <= n; i++)
			cnt[i] += cnt[i - 1];
		for (int i = n; i >= 1; i--)
			sa[cnt[a[i]]--] = i;
		rnk[sa[1]] = 1;
		for (int i = 2; i <= n; i++)
			rnk[sa[i]] = rnk[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]]);
		for (int k = 1; rnk[sa[n]] != n; k <<= 1) {
			for (int i = 1; i <= n; i++) {
				x[i] = rnk[i];
				y[i] = (i + k <= n) ? rnk[i + k] : 0;
			}
			memset(cnt, 0, sizeof(cnt));
			for (int i = 1; i <= n; i++)
				cnt[y[i]]++;
			for (int i = 1; i <= n; i++)
				cnt[i] += cnt[i - 1];
			for (int i = n; i >= 1; i--)
				rk[cnt[y[i]]--] = i;
			memset(cnt, 0, sizeof(cnt));
			for (int i = 1; i <= n; i++)
				cnt[x[i]]++;
			for (int i = 1; i <= n; i++)
				cnt[i] += cnt[i - 1];
			for (int i = n; i >= 1; i--)
				sa[cnt[x[rk[i]]]--] = rk[i];
			rnk[sa[1]] = 1;
			for (int i = 2; i <= n; i++)
				rnk[sa[i]] = rnk[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]);		
		}
		int now = 0;
		for (int i = 1; i <= n; i++) {
			if (now) now--;
			while (a[i + now] == a[sa[rnk[i] + 1] + now]) now++;
			height[rnk[i]] = now;
		}
		for (int i = 1; i <= n; i++)
			Min[i][0] = height[i];
		for (int p = 1; p < MAXLOG; p++) {
			int tmp = 1 << (p - 1);
			for (int i = 1, j = tmp + 1; j <= n; i++, j++)
				Min[i][p] = min(Min[i][p - 1], Min[i + tmp][p - 1]);
		}
		for (int i = 1; i <= n; i++) {
			bit[i] = bit[i - 1];
			if (i >= 1 << (bit[i] + 1)) bit[i]++;
		}
	}
	int lcp(int x, int y) {
		if (x == y) return N - x + 1;
		x = rnk[x], y = rnk[y];
		if (x > y) swap(x, y);
		int tmp = bit[y - x];
		return min(Min[x][tmp], Min[y - (1 << tmp)][tmp]);
	}
}
struct info {int pos, x, y; };
bool cmp(int x, int y) {
	return SuffixArray :: rnk[x] < SuffixArray :: rnk[y];
}
bool cnp(info x, info y) {
	return x.pos < y.pos;
}
int n, m, tot, a[MAXN];
int pos[MAXN], f[MAXN], cnt;
set <int> st[MAXN];
map <int, int> t;
ll sum[MAXN];
int F(int x) {
	if (f[x] == x) return x;
	else return f[x] = F(f[x]);
}
ll sqr(int x) {
	return x * (x + 1ll) / 2;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++) {
		read(a[++tot]);
		if (!t.count(a[tot])) t[a[tot]] = ++cnt;
		a[tot] = t[a[tot]];
	}
	SuffixArray :: init(a, tot);
	ll ans = 0;
	for (int j = 1; j <= m; j++) {
		for (int i = 1, p = j; i <= n; i++, p += m)
			pos[i] = p;
		sort(pos + 1, pos + n + 1, cmp);
		for (int i = 1; i <= n; i++) {
			f[i] = i;
			st[i].clear();
			st[i].insert(0);
			st[i].insert(i);
			st[i].insert(n + 1);
			sum[i] = sqr(i - 1) + sqr(n - i);
		}
		static info b[MAXN];
		for (int i = 1; i <= n - 1; i++) {
			int tmp = min(SuffixArray :: lcp(pos[i], pos[i + 1]), m - j + 1);
			b[i] = (info) {tmp + j - 1, (pos[i] - 1) / m + 1, (pos[i + 1] - 1) / m + 1};
		}
		sort(b + 1, b + n, cnp);
		b[n].pos = m;
		ll now = n * (n + 1ll) * (n + 2ll) / 6;
		for (int i = n - 1; i >= 1; i--) {
			ans += now * (b[i + 1].pos - b[i].pos);
			int tx = F(b[i].x), ty = F(b[i].y);
			if (st[tx].size() < st[ty].size()) swap(tx, ty);
			now += sum[tx], now += sum[ty];
			for (auto pos : st[ty])
				if (pos != 0 && pos != n + 1) {
					auto tmp = st[tx].insert(pos).first;
					auto pre = tmp, suf = tmp;
					pre--, suf++;
					sum[tx] -= sqr((*suf) - (*pre) - 1);
					sum[tx] += sqr((*suf) - (*tmp) - 1);
					sum[tx] += sqr((*tmp) - (*pre) - 1);
				}
			f[ty] = tx;
			now -= sum[tx] + n * (n + 1ll) / 2;
		}
		ans += now * (b[1].pos - j + 1);
	}
	writeln(ans);
	return 0;
}

**【HMAPPY】**Appy and Balloons

【思路要点】

  • 二分答案,贪心检验正确性。
  • 时间复杂度 O ( N L o g V ) O(NLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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 n; ll m, a[MAXN], b[MAXN];
bool valid(ll mid) {
	ll used = 0;
	for (int i = 1; i <= n; i++) {
		ll saved = mid / b[i];
		used += max(0ll, a[i] - saved);
	}
	return used <= m;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	for (int i = 1; i <= n; i++)
		read(b[i]);
	ll l = 0, r = 1e18;
	while (l < r) {
		ll mid = (l + r) / 2;
		if (valid(mid)) r = mid;
		else l = mid + 1;
	}
	writeln(l);
	return 0;
}

**【MINDSUM】**Minimize Digitsum

【思路要点】

  • 一个 1 0 10 10^{10} 以内的数经过至多 3 3 次数位和操作就会变成 9 9 以内的一个数。
  • 数位和操作不会改变一个数模 9 9 的余数。
  • 因此 3 + 9 + 4 3+9+4 次操作内,我们可以取到 1 1 9 9 中任意一个可能取到的数。
  • 因此我们直接暴力搜索 K 16 K\geq 16 层的决策即可。
  • 单组数据时间复杂度 O ( 2 K L o g N ) O(2^KLogN) ,取 K = 21 K=21

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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("");
}
ll ans, s, x, y;
ll f(ll x) {
	int ans = 0;
	while (x != 0) {
		ans += x % 10;
		x /= 10;
	}
	return ans;
}
void work(ll x, ll sum) {
	if (x < ans) {
		ans = x;
		s = sum;
	} else if (x == ans) chkmin(s, sum);
	if (sum >= 21) return;
	work(x + y, sum + 1);
	work(f(x), sum + 1);
}
int main() {
	int T; read(T);
	while (T--) {
		read(x), read(y);
		ans = x, s = 0;
		work(x, 0);
		printf("%lld %lld\n", ans, s);
	}
	return 0;
}

**【SURCHESS】**Chef and Surprise Chessboard

【思路要点】

  • 枚举一个正方形的左上角以及左上角的颜色,从小到大枚举其边长,通过部分和预处理,我们可以在 O ( 1 ) O(1) 的时间内知道需要的修改次数。
  • 再用前缀和处理一下答案数组即可。
  • N , M N,M 同阶时,时间复杂度 O ( N 3 + Q ) O(N^3+Q)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 205;
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 n, m, q;
char s[MAXN][MAXN];
int ans[MAXN * MAXN];
int row[MAXN][MAXN][MAXN][4];
int col[MAXN][MAXN][MAXN][4];
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		scanf("\n%s", s[i] + 1);
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++)
	for (int k = j; k <= m; k++) {
		memcpy(row[i][j][k], row[i][j][k - 1], sizeof(row[i][j][k]));
		row[i][j][k][(k % 2) * 2 + (s[i][k] == '1')]++;
	}
	for (int i = 1; i <= m; i++)
	for (int j = 1; j <= n; j++)
	for (int k = j; k <= n; k++) {
		memcpy(col[i][j][k], col[i][j][k - 1], sizeof(col[i][j][k]));
		col[i][j][k][(k % 2) * 2 + (s[k][i] == '1')]++;
	}
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++) {
		int now1 = 0, now2 = 0;
		for (int len = 1, ti = i, tj = j; ti <= n && tj <= m; len++, ti++, tj++) {
			now1 += row[ti][j][tj - 1][((len + j + 1) % 2) * 2 + 1];
			now1 += row[ti][j][tj - 1][((len + j) % 2) * 2];
			now2 += row[ti][j][tj - 1][((len + j) % 2) * 2 + 1];
			now2 += row[ti][j][tj - 1][((len + j + 1) % 2) * 2];
			now1 += col[tj][i][ti - 1][((len + i + 1) % 2) * 2 + 1];
			now1 += col[tj][i][ti - 1][((len + i) % 2) * 2];
			now2 += col[tj][i][ti - 1][((len + i) % 2) * 2 + 1];
			now2 += col[tj][i][ti - 1][((len + i + 1) % 2) * 2];
			now1 += s[ti][tj] == '1';
			now2 += s[ti][tj] == '0';
			chkmax(ans[now1], len);
			chkmax(ans[now2], len);
		}
	}
	for (int i = 1; i <= n * m; i++)
		chkmax(ans[i], ans[i - 1]);
	read(q);
	while (q--) {
		int x; read(x);
		chkmin(x, n * m);
		writeln(ans[x]);
	}
	return 0;
}

**【TREEWALK】**Walk on Tree

【思路要点】

  • 显然有 O ( N K ) O(NK) D P DP 做法及其 O ( N 3 L o g K ) O(N^3LogK) 的矩阵乘法优化。
  • O ( N K ) O(NK) D P DP 做法算出 K K 较小时的结果,并用 B e r l e k a m p M a s s e y Berlekamp-Massey 算法 计算出转移矩阵的特征多项式,具体做法可以参见 2017 2017 年国家集训队论文中毛啸论文的第 4 4 章。
  • C a y l e y H a m i l t o n Cayley-Hamilton 定理 ,其特征多项式为化零多项式,因此我们可以得到其转移矩阵 K K 次方的线性递推关系。
  • 借助 K K 较小时的结果,我们就可以在 O ( N ) O(N) 的时间内计算出答案的一项了。
  • 时间复杂度 O ( N 2 L o g K ) O(N^2LogK)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3005;
const int MAXM = 9005;
const int P = 998244353;
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("");
}
namespace LinearSequence {
	const int MAXN = 9005;
	const int MAXLOG = 31;
	const int P = 998244353;
	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++;
		}
		//cerr << a[cnt].size() << endl;
		if (n < 2 * a[cnt].size() + 5) {
			cerr << "Failed!" << endl;
			return;
		}
		memset(now, 0, sizeof(now));
		now[0][1] = 1; k = a[cnt].size();
		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]);
	}
	void query(int *res, int n) {
		if (n <= k) assert(false);
		res[0] = 1; n -= k;
		for (int p = 0; p < MAXLOG; p++) {
			int tmp = 1ll << p;
			if (n & tmp) times(res, res, now[p]);
		}
	}
}
vector <int> a[MAXN];
int dp[MAXM][MAXN];
int n, root, k;
int main() {
	read(n);
	for (int i = 1; i <= n - 1; i++) {
		int x, y; read(x), read(y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	read(root), read(k);
	dp[0][root] = 1;
	int goal = 2 * n + 15;
	for (int p = 1; p <= goal; p++) {
		for (int i = 1; i <= n; i++) {
			for (auto j : a[i]) {
				dp[p][i] += dp[p - 1][j];
				if (dp[p][i] >= P) dp[p][i] -= P;
			}
		}
	}
	if (k <= goal) {
		for (int i = 1; i <= n; i++)
			printf("%d ", dp[k][i]);
		return 0;
	}
	int tot = 0; static int tmp[MAXM];
	for (int p = 0; p <= goal; p++) {
		int val = 0;
		for (int i = 1; i <= n; i++)
			val = (97ll * val + dp[p][i]) % P; 
		tmp[++tot] = val;
	}
	static int res[MAXM];
	LinearSequence :: init(tot, tmp);
	LinearSequence :: query(res, k + 1);
	int len = LinearSequence :: k;
	for (int i = 1; i <= n; i++) {
		int ans = 0;
		for (int j = 0; j <= len - 1; j++)
			ans = (ans + 1ll * res[j] * dp[j + len - 1][i]) % P;
		printf("%d ", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/82973997