2018冬令营模拟测试赛(一)

[Problem A]全面战争不可避

试题描述

2018冬令营模拟测试赛(一)

补充说明:铁路毁坏指的是这条铁路彻底消失了,不会对之后的询问造成影响(即询问之间是独立的)

输入

2018冬令营模拟测试赛(一)

输出

2018冬令营模拟测试赛(一)

输入示例1

5 4 4
1 2
1 3
2 4
2 5
1 2
2 4
3 4
1 4

输出示例1

1
2
1
1

输入示例2 & 输出示例2

戳我下载

数据规模及约定

2018冬令营模拟测试赛(一)

题解

它保证每时每刻都是棵树,那么一条边贡献为 \(-1\),那么只需要数一下区间内出现了多少个不同的点就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
	if(Head == Tail) {
		int l = fread(buffer, 1, BufferSize, stdin);
		Tail = (Head = buffer) + l;
	}
	return *Head++;
}
int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 500010

int n, m, q, A[maxn<<1], pre[maxn<<1], lst[maxn];

int C[maxn<<1];
void update(int x) {
	for(; x <= (m << 1); x += x & -x) C[x]++;
	return ;
}
int sum(int x) {
	int sum = 0;
	for(; x; x -= x & -x) sum += C[x];
	return sum;
}

struct Que {
	int ql, qr, id;
	Que() {}
	Que(int _1, int _2, int _3): ql(_1), qr(_2), id(_3) {}
	bool operator < (const Que& t) const { return ql < t.ql; }
} qs[maxn];
int Ans[maxn];
struct Num {
	int lst, pos;
	Num() {}
	Num(int _, int __): lst(_), pos(__) {}
	bool operator < (const Num& t) const { return lst < t.lst; }
} ns[maxn<<1];

int num[50], cntn;
void writeint(int x) {
	if(!x) return (void)putchar('0');
	cntn = 0;
	while(x) num[cntn++] = x % 10, x /= 10;
	dwn(i, cntn - 1, 0) putchar(num[i] + '0');
	return ;
}

int main() {
	n = read(); m = read(); q = read();
	rep(i, 1, m) A[(i<<1)-1] = read(), A[i<<1] = read();
	
	rep(i, 1, m << 1) ns[i] = Num(lst[A[i]], i), lst[A[i]] = i;
	rep(kase, 1, q) {
		int ql = read(), qr = read();
		qs[kase] = Que(ql, qr, kase);
	}
	sort(qs + 1, qs + q + 1); sort(ns + 1, ns + (m << 1 | 1));
	int j = 1;
	rep(i, 1, q) {
		while(j <= (m << 1) && ns[j].lst < (qs[i].ql << 1) - 1) update(ns[j++].pos);
		Ans[qs[i].id] = sum(qs[i].qr << 1) - sum((qs[i].ql << 1) - 2) - (qs[i].qr - qs[i].ql + 1);
	}
	rep(i, 1, q) printf("%d\n", Ans[i]);
	
	return 0;
}

[Problem B]神社闭店之日

试题描述

2018冬令营模拟测试赛(一)

输入

2018冬令营模拟测试赛(一)

输出

2018冬令营模拟测试赛(一)

输入示例1

4 3 3 5
1 2 6 3
1 1
2 2
2 3
2 4
3 4

输出示例1

9
15
15
9
33

输入示例2 & 输出示例2

戳我下载

数据规模及约定

2018冬令营模拟测试赛(一)

注意:数据范围发生改动,所有 \(b_i\) 的最小公倍数现在小于等于 \(10^{13}\)

题解

首先用一下 KMP 中 border 的理论,不难发现满足要求的串必须是一个周期串,每个最小周期的长度就是所有 \(b_i(i \in [L_j, R_j])\) 和串长的最大公约数;于是现在有了一个暴力,令 \(G = \mathrm{gcd}\{b_i | i \in [L_j, R_j]\}\),则题目就是让求 \(\sum_{l=1}^L C^{\mathrm{gcd}(G, l)}\)

这样只能过 \(60\) 分,考虑优化,显然是枚举 \(\mathrm{gcd}\) 然后用莫比乌斯反演。

\begin{equation}
\sum_{l=1}^L C^{\mathrm{gcd}(G, l)} \\
= \sum_{g|G} { c^g \sum_{g|l, l \in [1, L]} {[\mathrm{gcd}(G, l) = g]} } \\
= \sum_{g|G} { c^g \sum_{l=1}^{\lfloor \frac{L}{g} \rfloor} {[\mathrm{gcd}(\frac{G}{g}, l) = 1]} } \\
= \sum_{g|G} { c^g \sum_{l=1}^{\lfloor \frac{L}{g} \rfloor} { \sum_{d|\frac{G}{g}, d|l} {\mu(d)} } } \\
= \sum_{g|G} { c^g \sum_{d|\frac{G}{g}} { \mu(d) \sum_{d|l, l \in [1, \lfloor \frac{L}{g} \rfloor]} {1} } } \\
= \sum_{g|G} { c^g \sum_{gd|G} { \mu(d) \lfloor \frac{L}{gd} \rfloor } } \\
\notag
\end{equation}

此时,令 \(T = gd\),我们看看能不能消掉一个字母。注意这时 \(g|T\)

\begin{equation}
= \sum_{g|G} { c^g \sum_{T|G, g|T} { \mu(\frac{T}{g}) \lfloor \frac{L}{T} \rfloor } } \\
= \sum_{T|G} { \lfloor \frac{L}{T} \rfloor \sum_{g|T} {c^g \mu(\frac{T}{g})} } \\
\notag
\end{equation}

其实字母个数并没有改变(去掉了一个 \(d\),但引入了 \(T\)),但是我们可以改变运算顺序了。可以发现,我们将枚举 \(T\) 放到了外层,这样后面那个 \(\sum_{g|T} {c^g \mu(\frac{T}{g})}\) 就只与 \(T\) 有关了,那么对于每一种可能的 \(T\),我们暴力枚举它的约数 \(g\),然后算出这个式子的值就好了。

可以发现后面询问出现的所有约数都是 \(\mathrm{lcm} \{ b_i | i \in [1, N] \}\) 的所有约数的真子集,所以我们预处理一下,再用个哈希,就可以 \(O(1)\) 询问了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
#define LL long long

LL read() {
	LL x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 100010
#define MOD 998244353

int n, L, C, q;
LL A[maxn];

LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }

LL G[maxn<<2];
void build(int o, int l, int r) {
	if(l == r) G[o] = A[l];
	else {
		int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
		build(lc, l, mid); build(rc, mid + 1, r);
		G[o] = gcd(G[lc], G[rc]);
	}
	return ;
}
LL query(int o, int l, int r, int ql, int qr) {
	if(ql <= l && r <= qr) return G[o];
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	LL ans = 0;
	if(ql <= mid) ans = gcd(ans, query(lc, l, mid, ql, qr));
	if(qr > mid) ans = gcd(ans, query(rc, mid + 1, r, ql, qr));
	return ans;
}

const int HMOD = 1000037, MAXN = 11010;
struct Hash {
	int ToT, head[HMOD], nxt[maxn], val[maxn];
	LL key[maxn];
	Hash(): ToT(0) { memset(head, 0, sizeof(head)); }
	int Find(LL x) {
		int u = x % HMOD;
		for(int e = head[u]; e; e = nxt[e]) if(key[e] == x) return val[e];
		return -1;
	}
	void Insert(LL x, int v) {
		if(Find(x) >= 0) return ;
		int u = x % HMOD;
		key[++ToT] = x; val[ToT] = v; nxt[ToT] = head[u]; head[u] = ToT;
		return ;
	}
} cp, mu, Ans;

int Pow(int a, LL b) {
	int ans = 1, t = a;
	while(b) {
		if(b & 1) ans = (LL)ans * t % MOD;
		t = (LL)t * t % MOD; b >>= 1;
	}
	return ans;
}

int getmu(LL x) {
	int cur = 1, cnt, m = (int)sqrt(x + .5);
	rep(i, 2, m) if(x % i == 0) {
		cnt = 0;
		while(x % i == 0) {
			if(++cnt > 1) return 0;
			x /= i;
		}
		cur = -cur;
		if(x == 1) break;
	}
	if(x > 1) cur = -cur;
	return cur;
}

int f[MAXN], cd;
LL divs[MAXN];
void init(LL x) {
	int m = (int)sqrt(x + .5);
	rep(i, 1, m) if(x % i == 0) {
		divs[++cd] = i;
		if(i != x / i) divs[++cd] = x / i;
	}
	sort(divs + 1, divs + cd + 1);
	rep(i, 1, cd) cp.Insert(divs[i], Pow(C, divs[i])), mu.Insert(divs[i], getmu(divs[i]));
	rep(i, 1, cd) {
		int s = 0;
		rep(j, 1, i) if(divs[i] % divs[j] == 0) {
			f[i] += (MOD + mu.Find(divs[i] / divs[j]) * cp.Find(divs[j])) % MOD;
			if(f[i] >= MOD) f[i] -= MOD;
			s += (LL)f[j] * (L / divs[j]) % MOD;
			if(s >= MOD) s -= MOD;
		}
		Ans.Insert(divs[i], s);
	}
	return ;
}

int main() {
	n = read(); L = read(); C = read(); q = read();
	LL lcm = 1;
	rep(i, 1, n) A[i] = read(), lcm = lcm / gcd(lcm, A[i]) * A[i];
	build(1, 1, n);
	init(lcm);
	
	while(q--) {
		int ql = read(), qr = read();
		printf("%d\n", Ans.Find(query(1, 1, n, ql, qr)));
	}
	
	return 0;
}

[Problem C]唯一神

试题描述

2018冬令营模拟测试赛(一)

2018冬令营模拟测试赛(一)

2018冬令营模拟测试赛(一)

输入

2018冬令营模拟测试赛(一)

输出

2018冬令营模拟测试赛(一)

输入示例1

2 2 2 0 1 2

输出示例1

300736801

输入示例2 & 输出示例2

戳我下载

数据规模及约定

2018冬令营模拟测试赛(一)

2018冬令营模拟测试赛(一)

题解

首先不难想到一个 dp,设 \(f(i, s, k)\) 表示对于前 \(i\) 行,第 \(i\) 行的连通状态为 \(s\)(这个连通状态最多有 \(9\) 种),当前连通块个数模 \(K\) 等于 \(k\) 时的概率。那么你可以预处理一下每两个连通性之间转移对答案的贡献,以及连通块的变化数量。

但是这样会发现每次修改都不得不重新 dp,非常费时。

考虑优化,我们发现每一步的转移都是类似的,即从 \(f(i, s, k)\) 转移到 \(f(i+1, s', k + \Delta k)\)(默认 \(k\) 这一维任何运算都在模 \(K\) 意义下),这样的话相当于乘上一个 \((maxs \cdot K) \times (maxs \cdot K)\)\(maxs\) 为状态数)的矩阵,于是我们可以用线段树维护这样一个矩阵,每次修改只需要改 \(\mathrm{log} n\) 个矩阵。但是这样做无论是时间还是空间都不够。

考虑进一步优化,由于每次 \(k\) 转移到下一步非常有规律,是循环位移了 \(\Delta k\) 个位置,我们就可以想到这是一个多项式循环卷积的形式。

补充一下循环卷积的定义(形式)和做法:

定义:令 \(f(x) = \sum_{i=0}^{K-1} {a_i x^i}\)\(g(x) = \sum_{i=0}^{K-1} {b_i x^i}\) 为两个 \(K-1\) 次多项式,那么它们的循环卷积 \((f \times g)(x) = \sum_{k=0}^{K-1} { x^k \sum_{i+j \equiv k (\mathrm{mod}\ K)} {a_i b_j} }\)

做法:循环卷积就是在 DFT 的时候开 \(K\) 位,点值直接对应位相乘,然后再保持 \(K\) 位并 IDFT 回来,得到的系数向量就是循环卷积之后的结果了。

由于转化成点值之后,每一步点值都是对应位相乘,所以 \(K\) 个点值之间互相独立,所以对于每个状态我们维护一下这个点值,再线段树中对于每个点值维护一个 \(maxs \times maxs\) 的矩阵,那么 dp 的过程就是将最初的状态转化成点值,\(F_k\) 是一个 \(maxs\) 维的向量,向量第 \(i\) 维存储连通状态为 \(i\) 时的第 \(k\) 维点值。那个转移矩阵可以这样构造:将每一次转移看成乘上一个 \(p \cdot x^{\Delta k}\) 的多项式(\(p\) 是系数,等于该转移贡献的概率),然后将这个多项式转化成点值表达,转移矩阵上对应位置填上对应维度的点值就好了。最后在把最终状态的多项式转化回系数向量即可,答案就是 \(k = 0\) 那一维。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 200010
#define maxs 10
#define maxk 10
#define maxnode 400000
#define MOD 370137601
#define G 37
#define LL long long

int n, m, K, q, p0, q0;

map <vector <int>, int> id;
vector <int> sta[maxs];
int cnts;
void search(vector <int> now) {
	if(id.count(now)) return ;
	sta[id[now] = ++cnts] = now;
	rep(i, 0, m - 1)
		rep(j, i + 1, m - 1) if(now[i] && now[j] && now[i] != now[j]) {
			vector <int> to = now;
			int c = min(to[i], to[j]);
			rep(k, 0, m - 1) if(to[k] == to[i] || to[k] == to[j]) to[k] = c;
			search(to);
		}
	return ;
}
int sleep[maxn][3], getup[maxn][3], fa[10], trans[maxs][maxs], tpow[maxs][maxs];
bool change[maxn][3];
int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); }
void getTrans() {
	vector <int> A(m), B(m);
	rep(sid, 1, cnts) {
		A = sta[sid];
		int cn = 0, id1[3], id2[3], cA = 0;
		memset(id1, 0, sizeof(id1));
		rep(i, 0, m - 1) if(A[i]) id1[i] = ++cn, cA = max(cA, A[i]);
		rep(s, 0, (1 << m) - 1) {
			int cnt = cn;
			memset(id2, 0, sizeof(id2));
			rep(i, 0, m - 1) if(s >> i & 1) id2[i] = ++cnt;
			rep(i, 1, cnt) fa[i] = i;
			rep(i, 0, m - 1) rep(j, i + 1, m - 1) if(A[i] == A[j] && A[i]) {
				int u = findset(id1[i]), v = findset(id1[j]);
				if(u != v) fa[v] = u;
			}
			rep(i, 0, m - 1) if(s >> i & 1) {
				if(A[i]) {
					int u = findset(id1[i]), v = findset(id2[i]);
					if(u != v) fa[v] = u;
				}
				if(i && (s >> i - 1 & 1)) {
					int u = findset(id2[i-1]), v = findset(id2[i]);
					if(u != v) fa[v] = u;
				}
			}
			int c = 0, cB = 0, num[10]; memset(num, 0, sizeof(num));
			rep(i, 0, m - 1) if(s >> i & 1) {
				int u = findset(id2[i]);
				if(!num[u]) num[u] = ++c;
			}
			rep(i, 1, cnt) if(findset(i) == i) cB++;
			rep(i, 0, m - 1) if(s >> i & 1) B[i] = num[findset(id2[i])]; else B[i] = 0;
			
			trans[sid][s] = id[B]; tpow[sid][s] = (cB - cA + K) % K;
		}
	}
	return ;
}

int Pow(int a, int b) {
	int ans = 1, t = a;
	while(b) {
		if(b & 1) ans = (LL)ans * t % MOD;
		t = (LL)t * t % MOD; b >>= 1;
	}
	return ans;
}

int Mat[maxk][maxk], _Mat[maxk][maxk], wk;
void FFT_init() {
	wk = Pow(G, (MOD - 1) / K);
	rep(i, 0, K - 1) rep(j, 0, K - 1) Mat[i][j] = Pow(wk, i * j), _Mat[i][j] = Pow(Mat[i][j], MOD - 2);
	return ;
}
LL buf[maxs];
void FFT(int *A, int tp) {
	memset(buf, 0, sizeof(buf));
	rep(i, 0, K - 1) {
		int *M = tp > 0 ? Mat[i] : _Mat[i];
		rep(j, 0, K - 1) buf[i] += (LL)*(M + j) * A[j] % MOD;
	}	
	if(tp < 0) rep(i, 0, K - 1) buf[i] = buf[i] % MOD * Pow(K, MOD - 2);
	rep(i, 0, K - 1) A[i] = buf[i] % MOD;
	return ;
}

struct Matrix {
	int A[maxs][maxs];
	Matrix() {}
	Matrix operator * (const Matrix& t) const {
		Matrix ans;
		rep(i, 1, cnts) {
			memset(buf, 0, sizeof(buf));
			const int *mA = A[i];
			rep(k, 1, cnts) {
				const int *tA = t.A[k];
				LL tmp = *(mA + k);
				rep(j, 1, cnts) buf[j] += tmp * *(tA + j) % MOD;
			}
			rep(j, 1, cnts) ans.A[i][j] = buf[j] % MOD;
		}
		return ans;
	}
} Pool[maxnode], Now[maxk];
int Arr[maxk], F[maxs][maxk];
int ToT;
struct SegTree {
	int node[maxn<<2];
	void build(int o, int l, int r, int level) {
		node[o] = level;
		if(l == r) return ;
		int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
		build(lc, l, mid, level - 1); build(rc, mid + 1, r, level - 1);
		return ;
	}
	void modify(int o, int l, int r, int x, int nid) {
		if(l == r) Pool[node[o] = ++ToT] = Now[nid];
		else {
			int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
			if(x <= mid) modify(lc, l, mid, x, nid); else modify(rc, mid + 1, r, x, nid);
			Pool[node[o] = ++ToT] = Pool[node[lc]] * Pool[node[rc]];
		}
		return ;
	}
} sgt[maxk];

int main() {
	n = read(); m = read(); K = read(); q = read(); p0 = read(); q0 = read();
	
	vector <int> A(m);
	rep(i, 0, (1 << m) - 1) {
		int c = 0;
		rep(j, 0, m - 1)
			if(i >> j & 1) {
				if(j && (i >> j - 1 & 1)) A[j] = c;
				else A[j] = ++c;
			}
			else A[j] = 0;
		search(A);
	}
	getTrans();
	
	FFT_init();
	int _sleep = (LL)p0 * Pow(q0, MOD - 2) % MOD, _getup = (LL)(q0 - p0) * Pow(q0, MOD - 2) % MOD, clog;
	for(clog = 1; (1 << clog) <= n; clog++); clog--;
	ToT = K * (clog + 1);
	rep(s, 1, cnts) {
		rep(tr, 0, (1 << m) - 1) {
			int t = trans[s][tr], p = 1;
			rep(j, 0, m - 1) if(tr >> j & 1) p = (LL)p * _getup % MOD; else p = (LL)p * _sleep % MOD;
			memset(Arr, 0, sizeof(Arr));
			Arr[tpow[s][tr]] = p;
			FFT(Arr, 1);
			rep(i, 0, K - 1) Pool[i*(clog+1)+1].A[s][t] = Arr[i];
		}
	}
	rep(i, 0, K - 1) {
		rep(j, 1, clog) Pool[i*(clog+1)+j+1] = Pool[i*(clog+1)+j] * Pool[i*(clog+1)+j];
		sgt[i].build(1, 1, n, (i + 1) * (clog + 1));
	}
	F[1][0] = 1;
	FFT(F[1], 1);
	// F[1~cnts][k] * Pool[sgt[k].node[1]]
	LL tB[maxs];
	rep(k, 0, K - 1) {
		memset(tB, 0, sizeof(tB));
		rep(i, 1, cnts) {
			int *pA = Pool[sgt[k].node[1]].A[i];
			rep(j, 1, cnts)
				tB[j] += (LL)F[i][k] * *(pA + j) % MOD;
		}
		rep(i, 1, cnts) F[i][k] = tB[i] % MOD;
	}
	int ans = 0;
	rep(i, 1, cnts) {
		FFT(F[i], -1);
		ans += F[i][0];
		if(ans >= MOD) ans -= MOD;
	}
	printf("%d\n", ans);
	
	while(q--) {
		int x = read(), y = read() - 1, p1 = read(), q1 = read();
		change[x][y] = 1;
		sleep[x][y] = (LL)p1 * Pow(q1, MOD - 2) % MOD; getup[x][y] = (LL)(q1 - p1) * Pow(q1, MOD - 2) % MOD;
		rep(s, 1, cnts) {
			rep(tr, 0, (1 << m) - 1) {
				int t = trans[s][tr], p = 1;
				rep(j, 0, m - 1)
					if(tr >> j & 1) p = (LL)p * (change[x][j] ? getup[x][j] : (LL)(q0 - p0) * Pow(q0, MOD - 2) % MOD) % MOD;
					else p = (LL)p * (change[x][j] ? sleep[x][j] : (LL)p0 * Pow(q0, MOD - 2) % MOD) % MOD;
				memset(Arr, 0, sizeof(Arr));
				Arr[tpow[s][tr]] = p;
				FFT(Arr, 1);
				rep(i, 0, K - 1) Now[i].A[s][t] = Arr[i];
			}
		}
		rep(i, 0, K - 1) sgt[i].modify(1, 1, n, x, i);
		
		memset(F, 0, sizeof(F));
		F[1][0] = 1;
		FFT(F[1], 1);
		// F[1~cnts][k] * Pool[sgt[k].node[1]]
		rep(k, 0, K - 1) {
			memset(tB, 0, sizeof(tB));
			rep(i, 1, cnts) {
				int *pA = Pool[sgt[k].node[1]].A[i];
				rep(j, 1, cnts)
					tB[j] += (LL)F[i][k] * *(pA + j) % MOD;
			}
			rep(i, 1, cnts) F[i][k] = tB[i] % MOD;
		}
		ans = 0;
		rep(i, 1, cnts) {
			FFT(F[i], -1);
			ans += F[i][0];
			if(ans >= MOD) ans -= MOD;
		}
		printf("%d\n", ans);
	}
	
	return 0;
}

又是一道常数卡得生活不能自理的题

相关文章:

  • 2022-01-06
  • 2021-10-01
  • 2021-05-31
  • 2021-10-22
  • 2021-07-11
  • 2021-12-07
  • 2021-07-14
  • 2021-06-27
猜你喜欢
  • 2021-08-14
  • 2022-01-06
  • 2021-12-02
  • 2021-08-30
  • 2022-01-17
  • 2021-11-19
  • 2022-02-05
相关资源
相似解决方案