【考试总结】欢乐模拟赛_Day2

\(T1\)

题目描述

在仙界中有着 \(n\) 位神仙, 每位神仙用一个 \(1 ∼ n\) 的特异编号表示, 老祖 \(ChitongZ\) 的编号为 \(1\) .
除去至尊至圣, 统管仙界的老祖 \(ChitongZ\) 外, 其他的 \(n − 1\) 位神仙都有唯一的尊师, 并且形成了一个树形结构.
老祖 \(ChitongZ\) 为了提升仙界综合实力, 倡导神仙们进行修炼.
但每次修炼是需要一颗金丹的, 若一位神仙自己没有金丹, 就需要向自己的尊师索取一颗. 若尊师也没有, 则尊师会向他的尊师索取一颗, 以此类推, 直到索取到一颗金丹, 修炼结束后这颗金丹就会被消耗掉.

\(Solution\)

预估 \(100pts\) ,数组开小了只有 \(60pts\)

其实这道题很多做法,我想了两个,都记录一下

\(EP.1\)

类似树剖,先做一个 \(dfs\) ,给每个点标上 \(dfs\)\(id[x]\),用线段树维护覆盖

对于一个点 \(x\) ,离它最近的被标记的点(即 \(ans\))是 所有可以覆盖它的点中 \(id\) 最大的,所以在每次线段树做 \(pushdown\) 的时候取 \(max\) ,输出的时候再转化为原答案

\(EP.2\)

用并查集维护

把所有操作保存下来,构造出一颗树,树的状态为所有操作完成后,再做一个 \(dfs\) ,若点 \(x\) 被标记过,则 \(s[x] = x\) ,不然指向 \(fa[x]\)

然后倒着操作,即给一个点标记变为去掉一个点的标记,若这个点没有标记了,则把 \(s[x]\) 指向 \(fa[x]\)

Code

只有 \(EP.2\) 的代码233

#include<bits/stdc++.h>
#define ll long long
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
int read();
const int N = 5e6 + 5;
const ll B1 = 37;
const ll P1 = 19260817;
const ll B2 = 137;
const ll P2 = 998244353; 
int n, m, x;
int s[N], d[N], fa[N], k[N]; 
ll ans1, ans2;
struct kk{
	int edge;
	char sta;
}Q[N];
int head[N], cnt;
struct node{
	int ver, next;
}a[N << 1];
void add(int x, int y)
{
	++ cnt;
	a[cnt].ver = y, a[cnt].next = head[x], head[x] = cnt;
}
void dfs(int x, int f)
{
	s[x] = d[x] ? x : f, fa[x] = f;
	for(int i = head[x]; i; i = a[i].next)
		if(a[i].ver != f)
			dfs(a[i].ver, x);	
}
int search(int x)
{
	return x == s[x] ? x : s[x] = search(s[x]);
}
int main()
{
	freopen("decomposition.in","r",stdin);
	freopen("decomposition.out","w",stdout);
	n = read(), m = read();
	F(i, 2, n) x = read(), add(x, i), add(i, x);
	F(i, 1, m)
	{
		Q[i].sta = read(), Q[i].edge = read();
		if(Q[i].sta == 2) ++ d[Q[i].edge];
	} 
	dfs(1, 0), s[1] = 1, fa[1] = 1;
	for(int i = m; i >= 1; -- i)
	{
		if(Q[i].sta == 1) k[i] = search(Q[i].edge);
		else 
		{
			-- d[Q[i].edge];
			if(! d[Q[i].edge]) s[Q[i].edge] = fa[Q[i].edge];
		}
	}
	F(i, 1, m) if(k[i])
		ans1 = (ans1 * B1 + k[i]) % P1, ans2 = (ans2 * B2 + k[i]) % P2;
	printf("%lld %lld", ans1, ans2);
	return 0;
}
int read()
{
	int x = 0;
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x;
}

\(T2\)

题目描述

\(n\) 个人依次进入观影厅, 观看 \(Weathering \ with \ You\) .
观影厅只有一排座位, 第 \(1\) 个人进入时, 会选择第 \(1\) 个座位坐下.
由于观影厅空调坏掉了, 十分炎热, 之后的每个人进入时, 他会选择一个离最近的已经坐下的人尽可能远的空位.
如果有多个空位使得离最近的人同样远, 则他会从其中随机选取一个.
若有两个人选择的位置相邻, 他们就会不满意.
为了让所有人都满意, 观影厅至少需要有几个座位呢?

\(Solution\)

根据各种打表找规律可得出

\[f(x) = x + 2^{1+⌊log_2(x−2)⌋}. \]

然后用高精度写

比较方便的写法是,用 \(2\) 不断往上乘,一边乘一边判断大小,最后加一个 \(n\)

这样就只要写高精乘低精,判断大小,高精度加法,比较好写

还有特殊情况,\(f(1) = 1\)\(f(2) = 3\),我没想到然后就只有 \(95pts\)

Code

#include<bits/stdc++.h>
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
int read();
const int N = 2e3 + 5;
char z;
int a[N], alen;
int b[N], blen;
int c[N], clen;
void mul(int b[])
{
	int x = 0;
	F(i, 1, blen) x += b[i] * 2, b[i] = x % 10, x = x / 10;
	if(x) b[++ blen] = x;
}
bool comp(int a[], int b[])
{
	int x = 2, clen = blen;
	F(i, 1, blen) c[i] = b[i];
	F(i, 1, clen)
	{
		c[i] += x, x = c[i] / 10, c[i] %= 10;
		if(! x) break;
	}
	if(x) c[++ clen] = x;
	if(alen != clen) return alen > clen;
	for(int i = alen; i >= 1; -- i)
		if(a[i] != c[i]) return a[i] > c[i];
	return true;
}
int main()
{
	freopen("tenki.in","r",stdin);
	freopen("tenki.out","w",stdout);
	while(scanf("%c", &z) != EOF) c[++ alen] = z - '0';
	-- alen;
	F(i, 1, alen) a[alen - i + 1] = c[i];
	if(alen == 1 && (a[1] == 1 || a[1] == 2))
	{
		puts(a[1] == 1 ? "1" : "3");
		return 0;
	}
	blen = 1, b[blen] = 2;
	while(comp(a, b)) mul(b);
	memset(c, 0, sizeof(c));
	F(i, 1, max(alen, blen))
	{
		c[i] = c[i] + a[i] + b[i];
		c[i + 1] = c[i] / 10, c[i] %= 10;
	}
	clen = max(alen, blen);
	if(c[clen + 1]) ++ clen;
	for(int i = clen; i >= 1; -- i) printf("%d", c[i]); 
	return 0;
}
/*
q = (log2 (n - 2)) + 1;
ans = 2 ^ q + n;
*/

\(T3\)

题目描述

给定一个正整数 \(k\) , 以及一棵 \(n\) 个节点的以 \(1\) 为根的有根树, 边有长度.
\(LCA(a, b)\) 表示 \(a\)\(b\) 在树上的最近公共祖先, \(dist(a)\) 表示树根到 \(a\) 的距离.
每个节点可以是黑色或白色, 初始时每个节点的颜色为白色.
进行 m 次操作, 每次操作是以下两种形式之一:
修改操作: 给出一个修改节点 \(x\) , 将节点 \(x\) 染上黑色. 保证 \(x\) 在染色前为白色.
询问操作: 给出一个询问节点 \(x\) , 记所有黑点形成的集合为 \(S\) , 求出下面式子的值:

\[\sum_{y∈S}F( dist ( LCA ( x, y ) ) ) \]

其中函数 \(F\) 定义为,

\[F(x) =\sum\limits_{i=1}^xi^k \]

由于答案可能很大, 只需要输出答案对 \(P = 998244353\) 取模的结果

\(Solution\)

这道题的难点:

\(1.\)题意比较绕,要求的答案函数套函数,很容易弄错
\(2.\)列出式子并把式子化简,将定值和要维护的值拆开

肝了半个下午 \(+\) 半个晚自习,心力交瘁...不过真的是道好题!!

\(Code\)

#include<bits/stdc++.h>
#define ll long long
#define ls k << 1
#define rs (k << 1) + 1
#define mid ((l + r) >> 1)
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
int read();
const int N = 1e5 + 5;
const int M = 1e7 + 5;
const int mod = 998244353;
int n, m, s;
ll x, y, num, vis[N], f[M], dist[N];
ll tree[N << 2], atree[N << 2], tag[N << 2], a[N];
ll dep[N], size[N], fa[N], son[N], id[N], top[N];
ll head[N], cnt, ver[N << 1], edge[N << 1], nxt[N << 1];
void add(int x, int y, int z)
{
	ver[++ cnt] = y, edge[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
}
ll qpower(ll x, int y)
{
	ll res = 1;
	while(y)
	{
		if(y & 1) res = res * x % mod;
		x = x * x % mod, y >>= 1;
	}
	return res;
}
void dfs1(int x, int ffa)
{
	dep[x] =dep[ffa] + 1, size[x] = 1, fa[x] = ffa;
	int maxn = -1;
	for(int i = head[x]; i; i = nxt[i])
		if(ver[i] != ffa)
		{
			dist[ver[i]] = dist[x] + edge[i], dfs1(ver[i], x), size[x] += size[ver[i]];
			if(size[ver[i]] > maxn) son[x] = ver[i], maxn = size[ver[i]];
		}
}
void dfs2(int x, int topfa, int ffa)
{
	id[x] = ++ num, top[x] = topfa;
	a[id[x]] = (f[dist[x]] - f[dist[ffa]] + mod) % mod;
	if(! son[x]) return;
	dfs2(son[x], topfa, x);
	for(int i = head[x]; i; i = nxt[i])
		if(ver[i] != fa[x] && ver[i] != son[x])
				dfs2(ver[i], ver[i], x);
}
void pushup(int l, int r, int k)
{
	atree[k] = (atree[ls] + atree[rs]) % mod;
}
void build(int l, int r, int k)
{
	if(l == r) 
	{
		tree[k] =  a[l] % mod;
		return;
	}
	build(l, mid, ls);
	build(mid + 1, r, rs);
	tree[k] = (tree[ls] + tree[rs]) % mod; 
}
void pushdown(int l, int r, int k)
{
	if(! tag[k]) return;
	atree[ls] = (tree[ls] * tag[k] % mod + atree[ls]) % mod;
	atree[rs] = (tree[rs] * tag[k] % mod + atree[rs]) % mod;
	tag[ls] += tag[k], tag[rs] += tag[k];
	tag[k] = 0;
}
void modify(int l, int r, int k, int x, int y, int v)
{
	if(l >= x && r <= y) 
	{
		atree[k] = (tree[k] * v % mod + atree[k]) % mod, tag[k] += v;
		return;
	}
	pushdown(l, r, k);
	if(x <= mid) modify(l, mid, ls, x, y, v);
	if(y > mid) modify(mid + 1, r, rs, x, y, v);
	pushup(l, r, k);
}
ll query(int l, int r, int k, int x, int y)
{
	if(l >= x && r <= y) return atree[k];
	pushdown(l, r, k);
	ll res = 0;
	if(x <= mid) res = (res + query(l, mid, ls, x, y)) % mod;
	if(y > mid) res = (res + query(mid + 1, r, rs, x, y)) % mod;
	return res;
}
void modify_range(int x)
{
	if(vis[x]) return;
	vis[x] = 1;	
	while(top[x] != 1)
	{
		modify(1, n, 1, id[top[x]], id[x], 1);
		x = fa[top[x]];
	}
	modify(1, n, 1, 1, id[x], 1);
}
ll query_range(int x)
{
	ll res = 0; 
	while(top[x] != 1)
	{
		res = (res + query(1, n, 1, id[top[x]], id[x])) % mod;
		x = fa[top[x]];
	}
	res = (res + query(1, n, 1, 1, id[x])) % mod;
	return res;
}
int main()	
{
	freopen("mafumafu.in","r",stdin);
	freopen("mafumafu.out","w",stdout);
	n = read(), m = read(), s = read();
	F(i, 1, M - 1) f[i] = (f[i - 1] + qpower(i, s) % mod) % mod;
	F(i, 2, n) x = read(), y = read(), add(x, i, y), add(i, x, y);
	dfs1(1, 0), dfs2(1, 1, 0), build(1, n, 1);
	while(m --)
	{
		y = read(), x = read();
		if(y == 1) modify_range(x);
		if(y == 2) printf("%lld\n", query_range(x));
	}
	return 0;
}
int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}

总结

排名\(:5/17\)

预估分数\(:100 + 100 + 0\)

实际分数\(:60 + 95 + 0\)

\(1.\)\(0202\) 了我怎么还犯数组开小了这种错误。。。
\(2.\)\(0202\) 了我怎么还反不考虑特殊情况这种错误。。。

总体来说今天还行,调整了考试状态,尽力地拿分,其实写完前两道题后还剩半个小时,打了 \(t3\) 的暴力,可惜没时间调了,不过这种习惯还是要坚持,抓住每一分每一秒

猜你喜欢

转载自www.cnblogs.com/Bn_ff/p/12682101.html