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

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

[Problem A]string

试题描述

TAT

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

一口一个前缀,二话不说建立 trie 树。

为了避免算重,我们在统计一个串的时候要求在最后的分割位置统计它,即不存在更靠后的分割位置。为了达到这一点我们先枚举后半部分,由于后半部分是某个串的一个前缀,即 trie 树上从根到某个点 (u) 的一条路径,那么现在就是要求前面填的串满足一个性质。

(v = fail[u]),即失配边指向的位置,(w) 是根节点到 (u) 的路径中刨去根节点到 (v) 的路径这个后缀的串所对应的节点(也即 (u) 在 trie 树上的 (k) 级祖先,(k) 为根到 (v) 的路径长度)。容易发现只要前面填的串不是 (v)失配树中的子树中的节点就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#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 1000010
#define maxa 30
#define maxlog 20
#define LL long long

char str[maxn];
int ToT, ch[maxn][maxa], dep[maxn], pa[maxn][maxlog];

void Insert(char *s) {
	int n = strlen(s), u = 1;
	rep(i, 0, n - 1) {
		int x = s[i] - 'a';
		if(!ch[u][x]) ch[u][x] = ++ToT;
		u = ch[u][x];
	}
	return ;
}
void build(int u) {
	rep(i, 1, maxlog - 1) pa[u][i] = pa[pa[u][i-1]][i-1];
	rep(c, 0, maxa - 1) if(ch[u][c]) {
		pa[ch[u][c]][0] = u;
		dep[ch[u][c]] = dep[u] + 1;
		build(ch[u][c]);
	}
	return ;
}
int KthPar(int u, int k) {
	rep(i, 0, maxlog - 1) if(k >> i & 1) u = pa[u][i];
	return u;
}
int fa[maxn], tofa[maxn], siz[maxn], Q[maxn], hd, tl;
void getfail() {
	hd = tl = 0; Q[++tl] = 1;
	while(hd < tl) {
		int u = Q[++hd];
		rep(c, 0, maxa - 1) if(ch[u][c]) {
			int v = ch[u][c], j = fa[u];
			while(j && !ch[j][c]) j = fa[j];
			fa[v] = ch[j][c] ? ch[j][c] : 1;
			if(fa[v] > 1) tofa[v] = KthPar(v, dep[fa[v]]);
			Q[++tl] = v;
		}
	}
	dwn(i, tl, 1) {
		int u = Q[i];
		siz[u]++; siz[fa[u]] += siz[u];
	}
	return ;
}

int main() {
	read();
	int n = read();
	ToT = 1;
	rep(i, 1, n) scanf("%s", str), Insert(str);
	build(1);
	getfail();
	
	LL ans = 0; siz[0] = 0;
	rep(i, 2, ToT) ans += ToT - siz[tofa[i]] - !tofa[i]; // (ToT - 1) - (siz[tofa[i]] - 1)
	printf("%lld
", ans);
	
	return 0;
}

[Problem B]tree

试题描述

QAQ

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

这是一道可以转化为基于深度维护信息的题,所以做法是长链剖分。

(f(i, j)) 表示节点 (i) 的子树中,还需要向子树 (i) 外部延伸 (j) 的距离能够组成的三元组的点对的贡献(这只是一个状态,具体计算还需要维护 (sum a_0 + a_1)(sum a_0a_1))。

在合并的时候我们可以暴力扫描一下其他子树的短链,然后把子树之间的贡献利用 (f(i, j)) 计算,同时合并更新 (f(i, j))。在重链上上移的时候,需要数组整体移位,这个东西记录一个移了多少位的变量就行了;在每一步移道位置 (0)(即 (j = 0) 那一位)时 (f(i, j)) 和当前节点也可以组成三元组,记得累计一下贡献。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#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 100010
#define maxm 200010
#define MOD 998244353
#define LL long long

int n, m, head[maxn], nxt[maxm], to[maxm], val[maxn];

void AddEdge(int a, int b) {
	to[++m] = b; nxt[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; nxt[m] = head[a]; head[a] = m;
	return ;
}

#define pll pair <LL, LL>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)
vector <pll> info[maxn]; // x: _sum{a0+a1}, y: _sum{a0*a1}
int mxd[maxn], son[maxn], dep[maxn], clo, dfn[maxn], uid[maxn], top[maxn];
void build(int u, int fa) {
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
		dep[to[e]] = dep[u] + 1;
		build(to[e], u);
		if(!son[u] || mxd[son[u]] < mxd[to[e]]) son[u] = to[e];
	}
	mxd[u] = max(mxd[son[u]], dep[u]);
	return ;
}
void gett(int u, int tp, int fa) {
	if(u == tp) info[u].resize(mxd[u] - dep[u] + 3 << 1);
	uid[dfn[u] = ++clo] = u; top[u] = tp;
	if(son[u]) gett(son[u], tp, u);
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && to[e] != son[u]) gett(to[e], to[e], u);
	return ;
}

LL ans, sumv[maxn], cntv[maxn];
void solve(int u, int fa) {
	if(son[u]) solve(son[u], u);
	int minus = mxd[u] - dep[u];
	// printf("solve(%d)
", u);
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && to[e] != son[u]) {
		solve(to[e], u);
		// printf("merge %d and %d
", u, to[e]);
		rep(h, 0, mxd[to[e]] - dep[to[e]]) {
			// printf("for height %d
", h);
			int i = h + 1 + minus, j = dfn[to[e]] + h;
			ans += info[top[u]][i].x * sumv[j] % MOD;
			ans += info[top[u]][i].y * cntv[j] % MOD;
			// printf("%d and %d: %lld * %lld + %lld * %lld = %lld
", u, to[e], info[top[u]][i].x, sumv[j], info[top[u]][i].y, cntv[j], info[top[u]][i].x * sumv[j] + info[top[u]][i].y * cntv[j]);;
			i = dfn[u] + h; j = h + 1 + mxd[to[e]] - dep[to[e]];
			ans += sumv[i] * info[to[e]][j].x % MOD;
			ans += cntv[i] * info[to[e]][j].y % MOD;
			// printf("also: %lld * %lld + %lld * %lld = %lld
", sumv[i], info[to[e]][j].x, cntv[i], info[to[e]][j].y, sumv[i] * info[to[e]][j].x + cntv[i] * info[to[e]][j].y);
			ans %= MOD;
		}
		(info[top[u]][minus].x += info[to[e]][mxd[to[e]]-dep[to[e]]+1].x) %= MOD;
		(info[top[u]][minus].y += info[to[e]][mxd[to[e]]-dep[to[e]]+1].y) %= MOD;
		rep(h, 1, mxd[to[e]] - dep[to[e]] + 1) {
			int i = h + minus, j = h + 1 + mxd[to[e]] - dep[to[e]], di = dfn[u] + h, dj = dfn[to[e]] + h - 1;
			info[top[u]][i].x += sumv[di] * cntv[dj] % MOD + sumv[dj] * cntv[di] % MOD;
			info[top[u]][i].y += sumv[di] * sumv[dj] % MOD;
			(info[top[u]][i].x += info[to[e]][j].x) %= MOD;
			// printf("+= %lld
", info[to[e]][j].x);
			(info[top[u]][i].y += info[to[e]][j].y) %= MOD;
			(sumv[di] += sumv[dj]) %= MOD; (cntv[di] += cntv[dj]) %= MOD;
			// printf("for height %d
merging: (a0+a1)%lld (a0*a1)%lld (sumv)%lld (cntv)%lld
", h, info[top[u]][i].x, info[top[u]][i].y, sumv[di], cntv[di]);
		}
	}
	sumv[dfn[u]] = val[u]; cntv[dfn[u]] = 1;
	(ans += info[top[u]][minus].x * val[u] % MOD + info[top[u]][minus].y) %= MOD;
	// printf("ans += %lld * %lld + %lld = %lld
", info[top[u]][minus].x, val[u], info[top[u]][minus].y, info[top[u]][minus].x * val[u] + info[top[u]][minus].y);
	return ;
}

int main() {
	read();
	n = read();
	rep(i, 1, n - 1) {
		int a = read(), b = read();
		AddEdge(a, b);
	}
	rep(i, 1, n) val[i] = read();
	
	build(1, 0);
	gett(1, 1, 0);
	/*rep(u, 1, n) {
		printf("[%d] top, son: %d, %d
", u, top[u], son[u]);
		if(top[u] == u) printf("vector_size: %d
", info[u].size());
	} // */
	
	solve(1, 0);
	
	printf("%lld
", ans);
	
	return 0;
}

[Problem C]heike

试题描述

QwQ

ToT

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

注意:本题给出的数据范围是假的,应为 (n,m le 10^6)

题解

这题先要证明一下(略过)是个拟阵,但是我觉得考虑替换也可以意识到它是个贪心了。

然后就是要上霍尔定理,这样我们可以得到:一个区间是满的当且仅当它所包含的 boys 总数等于区间长度。

每加入一个 boy 的时候维护一下那些区间满了,每个区间包含它的满区间最短的是谁(由于“满”有可交、可并性,证明略,故这个是可以求的),这个可以在每次加入成功时 dp 一下。

顺便维护一下一个区间中包含的最小权值的 boy,用于“假插入”询问。

对于没加入的 boy,在包含它的最短满区间上打个标记,表示它是备选的,到时候如果某个 boy 被删了导致那个区间不满,就可以把这个 boy 加进去。

最后把上面说的那个标记再 dp 下传一下,这样删掉一个 boy 就可以 (O(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--)

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 310
#define maxm 1000010
#define oo 2147483647
#define LL long long
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)

struct Boy {
	int l, r, v, id;
	Boy() {}
	Boy(int _1, int _2, int _3, int _4): l(_1), r(_2), v(_3), id(_4) {}
	bool operator < (const Boy& t) const { return v > t.v; }
} bs[maxm];
bool used[maxm];
int n, real[maxm], cb, q, tot[maxn][maxn], inMin[maxn][maxn], getMax[maxn][maxn];
pii Full[maxn][maxn];

void upd(pii& a, pii b) {
	if(!b.x) return ;
	if(!a.x || a.y - a.x + 1 > b.y - b.x + 1) a = b;
	return ;
}

int main() {
	read();
	n = read(); cb = read(); q = read();
	rep(i, 1, cb) {
		int l = read(), r = read(), v = read();
		bs[i] = Boy(l, r, v, i);
	}
	
	rep(i, 1, n) rep(j, i, n) inMin[i][j] = oo;
	LL ans = 0;
	sort(bs + 1, bs + cb + 1);
	rep(i, 1, cb) real[bs[i].id] = i;
	rep(i, 1, cb) {
		Boy& b = bs[i];
		if(!Full[b.l][b.r].x) {
			used[i] = 1;
			ans += b.v;
			rep(l, 1, b.l) rep(r, b.r, n) tot[l][r]++, inMin[l][r] = min(inMin[l][r], b.v);
			dwn(len, n, 1) rep(l, 1, n - len + 1) {
				int r = l + len - 1;
				if(tot[l][r] == r - l + 1) upd(Full[l][r], pii(l, r));
				if(len > 1) upd(Full[l][r-1], Full[l][r]), upd(Full[l+1][r], Full[l][r]);
			}
		}
		else {
			pii& fu = Full[b.l][b.r];
			getMax[fu.x][fu.y] = max(getMax[fu.x][fu.y], b.v);
		}
	}
	dwn(len, n, 2) rep(l, 1, n - len + 1) {
		int r = l + len - 1, now = getMax[l][r];
		getMax[l][r-1] = max(getMax[l][r-1], now);
		getMax[l+1][r] = max(getMax[l+1][r], now);
	}
	
	printf("%lld
", ans);
	while(q--) {
		int tp = read();
		if(tp == 1) {
			int nl = read(), nr = read(), nv = read();
			pii& fu = Full[nl][nr];
			printf("%lld
", ans - inMin[fu.x][fu.y] + max(inMin[fu.x][fu.y], nv));
		}
		if(tp == 2) {
			int i = real[read()];
			if(!used[i]){ printf("%lld
", ans); continue; }
			printf("%lld
", ans - bs[i].v + getMax[bs[i].l][bs[i].r]);
		}
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8177736.html