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

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

[Problem A]全面战争不可避

试题描述

QAQ

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

输入

TAT

输出

Q_Q

输入示例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

戳我下载

数据规模及约定

T_T

题解

它保证每时每刻都是棵树,那么一条边贡献为 \(-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]神社闭店之日

试题描述

QAT

输入

TAQ

输出

Q_T

输入示例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

戳我下载

数据规模及约定

T_T

注意:数据范围发生改动,所有 \(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]唯一神

试题描述

QwQ

ToT

QwT

输入

TwQ

输出

TwT

输入示例1

2 2 2 0 1 2

输出示例1

300736801

输入示例2 & 输出示例2

戳我下载

数据规模及约定

T_Q

Q_T

题解

首先不难想到一个 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;
}

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

posted @ 2017-12-20 08:39  xjr01  阅读(454)  评论(3编辑  收藏  举报