前言
这场比赛真是脑袋疼,耗了挺长时间的。后面的题解都放在这里了,需要自取。
第二场
1001 I love cube
给出一个边长为 n − 1 n-1 n−1的长方体,找出所有平行于三个坐标面的等边三角形,且坐标需为整数。
题解
容易发现,边长只能是斜着的,对每个边长计算,为 8 ∗ ( n − i ) 3 8*(n-i)^3 8∗(n−i)3,最终答案为 ∑ i = 1 n − 1 8 ∗ ( n − i ) 3 = 2 ∗ ( n ( n − 1 ) ) 2 \sum^{n-1}_{i=1}8*(n-i)^3=2*(n(n-1))^2 ∑i=1n−18∗(n−i)3=2∗(n(n−1))2.n需要使用int128。
1002 I love tree
这个是对树进行多种操作,问树的最终状态。是计算书。
题解
这个没太理解,看官方题解吧。
1003 I love playing games
这个一个决策类问题,判断有无最优解。
题解
这个题官方存在卡题,实际上使用动态规划复杂度为 O ( N 2 ) O(N^2) O(N2)
第三场
Bookshop
一个树的遍历问题。略。
第四场
Calculus
判断函数的收敛性,出一个做一个。
Kanade Loves Maze Designing
遍历树的。
题解
要保证复杂度低,能刷到 O ( N 2 ) O(N^2) O(N2)。用DFS做。
第五场
Miserable Faith
没看动,贴一下官方代码。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define mid (l + r >> 1)
#define lc (o << 1)
#define rc (o << 1 | 1)
typedef pair<int, int> pii;
typedef long long ll;
typedef double db;
inline ll getint()
{
char c = getchar();
ll ret = 0, f = 1;
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
ret = ret * 10 + c - '0', c = getchar();
return ret * f;
}
const int maxn = 1e5 + 10;
const int segn = maxn << 2;
vector<int> E[maxn];
int n, m;
int Fa[maxn], top[maxn], son[maxn], Siz[maxn], dep[maxn], dfn[maxn], tme;
int rt[maxn], ch[maxn][2], pfa[maxn], fa[maxn], siz[maxn];
ll sum[segn], at[segn], Ans;
inline void up(int o)
{
sum[o] = sum[lc] + sum[rc];
}
inline void addtag(int o, int l, int r, ll x)
{
at[o] += x; sum[o] += (r - l + 1) * x;
}
inline void down(int o, int l, int r)
{
if (at[o])
{
addtag(lc, l, mid, at[o]);
addtag(rc, mid + 1, r, at[o]);
at[o] = 0;
}
}
inline void modify(int o, int l, int r, int al, int ar, ll x)
{
if (al > ar) return;
if (al <= l && r <= ar) return addtag(o, l, r, x), void();
down(o, l, r);
if (al <= mid) modify(lc, l, mid, al, ar, x);
if (mid < ar) modify(rc, mid + 1, r, al, ar, x);
up(o);
}
inline ll query(int o, int l, int r, int al, int ar)
{
if (al > ar) return 0;
if (al <= l && r <= ar) return sum[o];
down(o, l, r);
ll ret = 0;
if (al <= mid) ret += query(lc, l, mid, al, ar);
if (mid < ar) ret += query(rc, mid + 1, r, al, ar);
return ret;
}
inline void clear(int o, int l, int r)
{
if (l > r) return;
sum[o] = at[o] = 0;
if (l == r) return;
clear(lc, l, mid);
clear(rc, mid + 1, r);
}
inline void maintain(int o)
{
siz[o] = siz[ch[o][0]] + siz[ch[o][1]] + 1;
rt[o] = ch[o][0] ? rt[ch[o][0]] : o;
}
inline void rotate(int x)
{
int o = fa[x], y = fa[o];
pfa[x] = pfa[o]; pfa[o] = 0;
int d = ch[o][1] == x ? 0 : 1;
ch[o][d ^ 1] = ch[x][d]; maintain(o);
if (ch[x][d]) fa[ch[x][d]] = o;
ch[x][d] = o; maintain(x);
fa[o] = x; fa[x] = y;
if (y) ch[y][ch[y][1] == o] = x, maintain(y);
}
inline void splay(int x)
{
for (int y = fa[x]; y; rotate(x), y = fa[x])
if (fa[y]) rotate((ch[y][1] == x) ^ (ch[fa[y]][1] == y) ? x : y);
}
inline void access(int x)
{
for (int u = x, v = 0; u; v = u, u = pfa[u])
{
splay(u);
if (ch[u][1])
{
int p = ch[u][1];
modify(1, 1, n, dfn[rt[p]], dfn[rt[p]] + Siz[rt[p]] - 1, 1);
Ans -= 1ll * siz[p] * (siz[u] - siz[p]);
fa[p] = 0, pfa[p] = u;
}
ch[u][1] = v; maintain(u);
if (v)
{
Ans += 1ll * siz[v] * (siz[u] - siz[v]);
modify(1, 1, n, dfn[rt[v]], dfn[rt[v]] + Siz[rt[v]] - 1, -1);
fa[v] = u, pfa[v] = 0;
}
}
}
inline void dfs(int u)
{
dfn[u] = ++tme; son[u] = 0;
dep[u] = dep[Fa[u]] + 1; Siz[u] = 1;
for (auto v : E[u])
{
if (v == Fa[u]) continue;
Fa[v] = u; dfs(v); Siz[u] += Siz[v];
if (Siz[v] > Siz[son[u]]) son[u] = v;
}
}
inline void cut(int u)
{
if (son[u]) top[son[u]] = top[u], cut(son[u]);
for (auto v : E[u])
if (v != Fa[u] && v != son[u]) top[v] = v, cut(v);
}
inline int getlca(int u, int v)
{
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = Fa[top[u]];
}
return dep[u] > dep[v] ? v : u;
}
inline void solve()
{
Ans = tme = 0; clear(1, 1, n);
rep(i, 1, n) E[i].clear(), ch[i][0] = ch[i][1] = 0, fa[i] = 0;
n = getint(); m = getint();
rep(i, 1, n - 1)
{
int u = getint(), v = getint();
E[u].pb(v); E[v].pb(u);
}
dfs(1); top[1] = 1; cut(1);
rep(i, 2, n) pfa[i] = Fa[i];
rep(i, 1, n) siz[i] = 1, rt[i] = i;
rep(i, 2, n)
modify(1, 1, n, dfn[i], dfn[i] + Siz[i] - 1, 1);
while (m--)
{
int typ = getint();
if (typ == 1)
{
access(getint());
int tmp = getint();
}
if (typ == 2)
{
int u = getint(), v = getint(), lca = getlca(u, v);
ll ans = query(1, 1, n, dfn[u], dfn[u]) + query(1, 1, n, dfn[v], dfn[v]);
ans -= query(1, 1, n, dfn[lca], dfn[lca]) << 1;
printf("%lld\n", ans);
}
if (typ == 3)
{
int u = getint();
ll ans = query(1, 1, n, dfn[u], dfn[u] + Siz[u] - 1);
ans -= query(1, 1, n, dfn[u], dfn[u]) * Siz[u];
printf("%lld\n", ans);
}
if (typ == 4) printf("%lld\n", Ans);
}
}
int main()
{
int t = getint();
while (t--) solve();
return 0;
}
VC is all you need
我一直有一个思想,就是这些程序设计题目总是写的很“傲慢”,总是不经意间说一些能唬住我们这些小萌新的词语。比如,all you need的梗,不理解可以见这篇:https://zhuanlan.zhihu.com/p/396545047。
说会这个题,这是一个分割问题。可以改一下看题的角度:
n
n
n维空间中,求
m
m
m的最大值,使得你可以找到
m
m
m个点(自己给定坐标),满足:
无论对这 m m m个点如何二染色,也就是对于 2 m 2^m 2m种染色方案中的每一种,都总存在一个 n − 1 n-1 n−1维超平面,严格分开这两种颜色的点。
而我们有:
m m a x = n + 1 m_{max}=n+1 mmax=n+1
故题易。
第六天
Problem A. Yes, Prime Minister
这个题是找一个连续整数组,包含数
x
x
x,并且,它们的和是质数。输入
x
x
x,输出数组的长度;如果没有则输出-1。
注意到,是全体整数。
注意到等差数列的概念,故数组长度为1或2。称为选择题。选择一下即可。
注:先用线性筛求出质数。
Problem B. Might and Magic
对战模拟。列处函数即可发现是一个下凸函数。极值必在两端点取得。求极值即可。
第七天
Problem 1002. Fall with Fake Problem
两兵对战,注意到串长为奇数。由于每次我们删除每个1的左右两边的数字,因此总串长奇偶性
不变。容易发现,如果有一种操作方案使得最后剩下的全为1,那么我们可以将这个剩下的串删至只剩一个1,而如果我们有一种方案可以将该串删至只剩一个1,这种方案肯定是满足题目条件的,因此我们可以认为题目所求的条件与将串删至只剩一个1的条件是等价的。现在我们来考虑一个串满足什么样的条件才能够有一种方案删至只剩一个1。容易发现,如果这个串中间位置有一个1,那么我们可以一直操作这个1达成条件。而如果这个串中间位置没有1,我们考虑中间一段0能够被删除的条件。串中每个1最多能操作的次数为其距离最近的边缘的距离,例如0010000最多只能操作两次,有一个非常显然的结论是我们应当选择尽可能靠近中心的1。我们取出最靠近中心的左右两边的两个1,分别考
虑操作它能够删除的区间,当两个删除区间覆盖整个串的时候,我们发现此时必然有一种操作方案能够使整个串被删至只剩一个1 ,而如果不能覆盖整个串,必然有一个位于两个1中间的0剩下,而且无论如何操作都无法将其删除 。条件最后总结为:最靠近中线左右两侧的两个1距离不超过
(
n
−
3
)
/
2
(n-3)/2
(n−3)/2。得到了条件之后就可以进行计数,设待填位置总数为 ,分情况讨论:
- 若正中间有1则可任意填数,答案即为 2 t o t 2^{tot} 2tot
- 若正中间没有1,我们沿中线将区间划分为左右两块,设左区间第 个位置左侧问号的数量为 ,在右区间内距离该位置不超过 的问号的数量为 ,左区间最靠右的1位置为 ,右区间最靠左的1位置为 ,右区间问号总数为 ,考虑枚举左区间最靠右的1的位置,则有:
- 对于 i < l o c l i<locl i<locl,不存在有问号填1后满足条件,因此不产生贡献
- 对于
i
>
=
l
o
c
l
i>=locl
i>=locl ,满足条件的情况为该点必须为1,其右侧全填0 ,并且右区间至少有一个1满足距离限制,讨论 与其位置关系得到两种情况答案为
- 2 c n t l i + t o t r − c n t r i ( 2 i c n t r − 1 ) = 2 c n t l i + t o t r − 2 c n t l i + t o t r − c n t r i 2^{{cntl}_i+totr-cntr_i}(2^{cntr}_i-1)=2^{{cntl}_i+totr}-2^{{cntl}_i+totr-cntr_i} 2cntli+totr−cntri(2icntr−1)=2cntli+totr−2cntli+totr−cntri
-
2
c
n
t
l
i
+
t
o
t
r
2^{{cntl}_i+totr}
2cntli+totr
容易发现,我们需要维护的信息为左区间每个问号位置的 2 c n t l i + t o t r 2^{{cntl}_i+totr} 2cntli+totr与 2 c n t l i + t o t r − c n t r i 2^{{cntl}_i+totr-cntr_i} 2cntli+totr−cntri,对于 l o c l locl locl处的答案,还需维护 c n t l i cntl_i cntli和 c n t r i cntr_i cntri,改变一个位置的字符,将会产生对于一个区间信息乘除2或加减1的一些影响,我们可以简单地使用线段树完成以上信息的维护,并在询问操作的时候使用以上信息分类讨论求得答案。
第八天
Problem A. X-liked Counting
这个像数论,其实不如判断字符串,调用方法就行。注意要应用容斥原理做,要不超时。
Problem B. Buying Snacks
求种类类型题。动态规划。
设 表示当前考虑完前 个零食,一共用了 块钱的方案数,可以得出复杂度为 的转移方程:
考虑如何去优化这个转移。
第一个思路是快速乘法幂,第二个是倍增思想。
第一个很简单,第二个没想出来也没听懂,记录一下。
后记
这次训练大概持续了一个月,每次能够做出来的题寥寥无几,几度想要放弃。但是____让我坚持了下来。未来加油。