联赛前第五阶段总结
上次第8,铜牌里第一,这次终于银牌了!!虽然是压线
然而竹兄挑战并暴踩了我(AK佬挑战菜鸡,还让不让人活了QwQ)
王若竹 暴揍 周骁坤 297分,周骁坤仍有再战之力
周骁坤 轻虐 lgx 172分,lgx失去挑战资格
总结
第五阶段模拟赛总结
上次第8,铜牌里第一,这次终于银牌了!!虽然是压线
“王若竹 暴揍 周骁坤”
然而竹兄挑战并暴踩了我(AK佬挑战菜鸡,还让不让人活了QwQ)
这阶段总体来说感觉还不错,比上次进步了一名,挂的分也不是很多,除去求直径没考虑全挂的56分之外,
就只有漏了一行输出挂的40分,比起之前不知道少了多少倍
感觉这个阶段的题好像难了许多,尤其是白天的题。满分是晚上两倍的白天考的成绩仅仅是晚上的1.1616766467065869倍,
这真是太离谱了
而且题也有好多改不出来的,有的题思想懂了,但就是写不出来,还是马力不够。
我现在感觉我有一个小小的进步就是在考场上敢于去推式子了,
有道题式子推了一半,没推到最终形态还用线段树卡过去了,还有道题式子推对了不会实现,就拿了50分
之前只要是数学题就马上打一个纯暴力模拟一遍题意就赶紧下一道题了,现在看看有点推式子的真的不是很难。
然后感觉自己自信心增强了,考场上也能A题了,感觉特别不错。
关于讨论,确实是影响到改题的时间,但是老师说的讲题的时候有什么不会的就要问,问也确实是问了,思想也理解了一些,
可真到落实到代码的时候就又会出现新的问题,而且都进行到这儿了,因为一个问题一道题就过不去了,
反正我是感觉挺不舒服,还有的题根本就不在能力范围之内,根本就没啥可提问的,基础都不会
下个阶段我得注意一下这个事情,尽量减少讨论的时间和频率,能不窜机房就不窜机房,争取更高效一些,
多了1年时间也没能弥补脑子不好使的缺陷,还是得多加把劲
晚间测试9
A 学数数
-
求max的时候要找到左面第一个大于a[i]的数,找到右面第一个大于等于a[i]的数,二分找一下,询问的时候离散化。
-
下了考场说可以单调栈,我一下就明白楼房重建为啥叫线段树维护单调栈了
B 主仆见证了 Hobo 的离别
-
看这题的时候就剩30分钟了,连题都读错了,以为每个元件内一定不会有相同的记忆,然后就写了个bitset,还拿了36分?我直接疑惑
-
正解是每个新节点与和它直接相关的点连有向边,维护出一颗树来
-
离线询问,判断在树上的父子关系,如果x是y的祖先且x到y路径上全是交,或y是x的祖先且y到x路径上全是并,就输出1
-
k=1的时候交和并是一样的,需要特判一下,维护一个并查集,有k=1的情况就把这两个点并到一起就好了。
联赛模拟测试18
A 施工
-
开始一个小时都在想T1,gjk的那种方法其实我也有想过,只不过那时以为建筑可以增高也可以降低,就把这种方法pass掉了,结果到了8点我打出暴力Dfs才发现我读错题了,只能增不能减,然后我就把0分Dfs改过了样例就赶紧去写别的题
-
正解是单调栈优化DP。
-
考虑一个地方只有比两面都低,施工才可能使答案变优。
-
定义f[i]为前i幢建筑,第i幢建筑不施工的不美观度与话费的最小和,从f[j]转移过来,要保证j+1到i-1中有比两端低的建筑才可以,转移如下:
- 后面的min化成二次函数通式可以通过二次函数性质找到一个t使得式子最小,化简后的式子:
- 用单调栈优化转移就好了。
Code
Show Code
#include <cstdio>
#include <algorithm>
#define abs(a) ({int xx = a; xx < 0 ? -xx : xx;})
typedef long long ll;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, m, h[N], stk[N], tp;
ll s[N], s2[N], f[N];
//f[i]:前i幢建筑,第i幢建筑不施工的不美观度与话费的最小和
ll Cal(int i, int j, int k) {
ll a = i - j - 1;
ll b = (s[i-1] - s[j]) * 2;
ll c = s2[i-1] - s2[j];
if (i != n + 1) b += m, c += 1LL * m * h[i];
if (j != 0) b += m, c += 1LL * m * h[j];
int t = (1.0 * b / a / 2 + 0.5);
t = std::max(t, h[k]);
t = std::min(t, std::min(h[i], h[j]));
return a * t * t - b * t + c;
}
int main() {
freopen("construct.in", "r", stdin);
freopen("construct.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; ++i) {
h[i] = read();
s[i] = s[i-1] + h[i];
s2[i] = s2[i-1] + 1LL * h[i] * h[i];
}
h[0] = 1e9; h[n+1] = 1e9 - 1;
stk[++tp] = 0;
for (int i = 1; i <= n + 1; ++i) {
f[i] = f[i-1];
if (i != 1 && i != n + 1) f[i] += 1LL * m * abs(h[i] - h[i-1]);
for (; tp && h[stk[tp]] <= h[i]; --tp)
f[i] = std::min(f[i], f[stk[tp-1]] + Cal(i, stk[tp-1], stk[tp]));
stk[++tp] = i;
}
printf("%lld\n", f[n+1]);
return 0;
}
B 蔬菜
-
一看多组询问,然后求的是每种菜出现次数的平方和,就很想莫队,然后我粗略的算了一下时间复杂度,感觉很惊人,然后就打的纯暴力。最后10分钟检查的时候发现还有20分的数据可做,然后多拿了20分。
-
正解是4维偏序,但是好像被卡了,然后就只能二维莫队了。
-
主函数部分好写,主要是排序这里,可以奇偶优化一下
bool operator < (const Node &b) const {
int u1 = (u - 1) / sqn + 1, u2 = (b.u - 1) / sqn + 1;
int d1 = (d - 1) / sqn + 1, d2 = (b.d - 1) / sqn + 1;
int l1 = (l - 1) / sqm + 1, l2 = (b.l - 1) / sqm + 1;
return u1 == u2 ? d1 == d2 ? l1 == l2 ? l1 & 1 ? r < b.r : r > b.r : d1 & 1 ? l1 < l2 : l1 > l2 : u1 & 1 ? d1 < d2 : d1 > d2 : u1 < u2;
}
C 联盟
-
题目中的危险程度就是直径
-
枚举每条边断掉,然后将两个树的直径的中点连起来,然后我就非常sb的认为新树的直径\(d=\left \lceil \frac{d1}{2} \right \rceil+\left \lceil \frac{d1}{2} \right \rceil+1\),于是拿到了4分的好成绩。
-
其实还有可能原来的两个直径它更长,于是新树的直径应该是\(d=\max\{ d1, d2,\left \lceil \frac{d1}{2} \right \rceil+\left \lceil \frac{d1}{2} \right \rceil+1 \}\),看到题解后我立马反应过来,改了这个地方后就60分了QwQ
-
本来想O(n)预处理出分别以直径上每条边两端点为根的子树的直径,由于太菜了,改成了用线段树维护。线段树维护特别简单
-
我为了快一点用的Lca转RMQ,能把总负责度降到\(O(n\log n)\)级别,后来发现这个用的是欧拉序,线段树维护得用dfs序,然后进行了一番魔改,代码就变的极度的丑了
-
这道题懂了思路就很好写了,就是不好调。
Code
Show Code
#include <cstdio>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
const int N = 3e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int next, t;
}e[N<<1];
int head[N], edc = 1;
void Add(int x, int y) {
e[++edc] = (Edge) {head[x], y};
head[x] = edc;
}
struct Tree {
int x, y, d;
}t[N<<2];
int n, p[N], tot, b[N], cnt, ans = 1e9;
int fa[N], to[N], dep[N], siz[N], a[N], dfn[N], dfc;
struct ST {
int dfn[N], dfc, lg[N<<1], f[21][N<<1];
void Init() {
for (int i = 2; i <= dfc; ++i)
lg[i] = lg[i>>1] + 1;
for (int i = 0; i < lg[dfc]; ++i) {
for (int x = 1; x + (1 << i + 1) - 1 <= dfc; ++x) {
const int y = x + (1 << i);
f[i+1][x] = dep[f[i][x]] < dep[f[i][y]] ? f[i][x] : f[i][y];
}
}
}
int Lca(int x, int y) {
x = dfn[x]; y = dfn[y];
if (x > y) std::swap(x, y);
int k = lg[y-x+1];
y = y - (1 << k) + 1;
return dep[f[k][x]] < dep[f[k][y]] ? f[k][x] : f[k][y];
}
}st;
void Dfs(int x) {
dep[x] = dep[fa[x]] + 1;
st.f[0][++st.dfc] = x;
st.dfn[x] = st.dfc;
dfn[x] = ++dfc;
a[dfc] = x;
siz[x] = 1;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].t;
if (y == fa[x]) continue;
fa[y] = x; to[y] = i;
Dfs(y);
st.f[0][++st.dfc] = x;
siz[x] += siz[y];
}
}
Tree operator + (const Tree &a, const Tree &b) {//合并两个联通块,求直径两端点
Tree c = a.d > b.d ? a : b;
int x[] = {a.x, a.y}, y[] = {b.x, b.y};
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
int d = dep[x[i]] + dep[y[j]] - 2 * dep[st.Lca(x[i], y[j])];
if (c.d < d) c = (Tree) {x[i], y[j], d};
}
}
return c;
}
void Build(int rt, int l, int r) {
if (l == r) return t[rt] = (Tree) {a[l], a[l], 0}, void();
int mid = l + r >> 1;
Build(ls, l, mid); Build(rs, mid + 1, r);
t[rt] = t[ls] + t[rs];
}
Tree Ask(int rt, int l, int r, int x, int y) {
if (x <= l && r <= y) return t[rt];
int mid = l + r >> 1;
if (y <= mid) return Ask(ls, l, mid, x, y);
else if (x > mid) return Ask(rs, mid + 1, r, x, y);
return Ask(ls, l, mid, x, y) + Ask(rs, mid + 1, r, x, y);
}
int Jump(int x, int d) {
while (d--) x = fa[x];
return x;
}
int main() {
freopen("league.in", "r", stdin);
freopen("league.out", "w", stdout);
n = read();
for (int i = 1; i < n; ++i) {
int x = read(), y = read();
Add(x, y); Add(y, x);
}
Dfs(1);
st.Init();
Build(1, 1, n);
int x = t[1].x, y = t[1].y, lca = st.Lca(x, y);
if (dep[x] < dep[y]) std::swap(x, y);
for (; dep[x] > dep[lca]; x = fa[x])
p[++tot] = to[x] / 2;
for (; dep[y] > dep[lca]; y = fa[y])
p[++tot] = to[y] / 2;//p数组记录直径上每条边的编号
for (int i = 1; i <= tot; ++i) {
x = e[p[i]*2].t; y = e[p[i]*2+1].t;
if (fa[x] == y) std::swap(x, y);
Tree t1 = Ask(1, 1, n, 1, dfn[y] - 1);
if (dfn[y] + siz[y] <= n) t1 = t1 + Ask(1, 1, n, dfn[y] + siz[y], n);
Tree t2 = Ask(1, 1, n, dfn[y], dfn[y] + siz[y] - 1);
int d = std::max((t1.d + 1 >> 1) + (t2.d + 1 >> 1) + 1, std::max(t1.d, t2.d));//求新图的最小直径
if (ans > d) ans = d, b[cnt=1] = p[i];
else if (ans == d) b[++cnt] = p[i];//b数组记录输出方案
}
if (ans == t[1].d) {//如果答案不会更优,所以边都可以形成合法方案
printf("%d\n%d", ans, n - 1);
for (int i = 1; i < n; ++i)
printf(" %d", i);
printf("\n%d %d %d %d\n", e[2].t, e[3].t, e[2].t, e[3].t);
return 0;
}
printf("%d\n%d", ans, cnt);
std::sort(b + 1, b + cnt + 1);
for (int i = 1; i <= cnt; ++i)
printf(" %d", b[i]);
x = e[b[1]*2].t, y = e[b[1]*2+1].t;
printf("\n%d %d ", x, y);
if (fa[x] == y) std::swap(x, y);
Tree t1 = Ask(1, 1, n, 1, dfn[y]);
if (dfn[y] + siz[y] < n) t1 = t1 + Ask(1, 1, n, dfn[y] + siz[y], n);
Tree t2 = Ask(1, 1, n, dfn[y], dfn[y] + siz[y] - 1);
if (dep[t1.x] < dep[t1.y]) std::swap(t1.x, t1.y);
if (dep[t2.x] < dep[t2.y]) std::swap(t2.x, t2.y);
printf("%d %d\n", Jump(t1.x, t1.d + 1 >> 1), Jump(t2.x, t2.d + 1 >> 1));//跳到直径中点
return 0;
}
D 水滴
-
不明白为啥把签到题放在最后,一眼看上去就是双指针扫一遍,O(n)就完事了
-
忍不住吐槽一下出题人,水滴攻击地球战舰还用牺牲?都排成一排了还用的招转向?直接一头撞过去不就完了嘛,
题目与背景完全不符
晚间测试8
A chinese
-
推式子,考场上只推出任意一个位置是炼字的方案数是\(\sum_{i=1}^{k}(i-1)^{n+m-2}k^{nm-n-m+1}\),然后就不会了,就写了个暴搜拿了45分。
-
正解是考虑最终答案\(\sum_{i=0}^{nm}i\times f_i\)的含义:所有方案中炼字的个数之和,然后把每个位置是炼字的方案数加起来就行了
B physics
-
nm用DP求解一个询问的我还不会,就打了个O\((nm\log n)\) 的,暴力询问,然后就拿了70分
-
终于学会了nm求解一个询问,而且这么简单。
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (c[i][j] == '-') continue;
f[i][j] = std::min(f[i-1][j-1], std::min(f[i][j-1], f[i-1][j])) + 1;
}
}
-
然后维护两个数组up[i][j]表示向上最多扩展几个, down[i][j]表示向下最多扩展几个,
-
运用
唐舞麟的时光回溯领域,倒着加进去,答案一定不降。 -
枚举答案是否合法,单调队列滑动窗口一下就可以了,答案一定不会超过n,总的枚举是O(n)级别的
联赛模拟测试17
今天挂了40分呀,好像好长时间没有挂分了
A 简单的区间
-
看这种题老感觉应该是线段树,想了半天都没想出来,只能拿30分纯暴力
-
真就是CDQ板子题,考虑跨过mid的区间的贡献,先让最大值在左侧,在让最大值在右侧
void Solve(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
Solve(l, mid); Solve(mid + 1, r);
int i = mid, j = mid + 1, mx = 0;
for (; i >= l; --i) {//mx in l mid
mx = std::max(mx, a[i]);
for (; j <= r && a[j] <= mx; ++j)
b[(s[j] - s[mid])%k]++;
int x = (s[mid] - s[i-1] - mx) % k;
if (!x) ans += b[0];
else ans += b[k-x];
}
for (--j; j >= mid + 1; --j)
b[(s[j] - s[mid])%k] = 0;
i = mid, j = mid + 1, mx = 0;
for (; j <= r; ++j) {//mx in mid+1 r
mx = std::max(mx, a[j]);
for (; i >= l && a[i] < mx; --i)
b[(s[mid]-s[i-1])%k]++;
int x = (s[j] - s[mid] - mx) % k;
if (!x) ans += b[0];
else ans += b[k-x];
}
for (++i; i <= mid; ++i)
b[(s[mid] - s[i-1])%k] = 0;
}
B 简单的玄学
-
自己瞎推了个式子,拿了50分。
-
求至少两个相同,那就减去完全不相同的概率,完全不同的方案数是\(A_{2^n}^{m}\),化简一下,最终的答案就是
- 题目中还要求先约到最简再取模,分母的质因子只有2,就看分子中2的个数,其实就是从1到m-1中2的个数,log m即可解决,然后乘上逆元就行了
scanf("%lld%lld", &n, &m);
if (n < log2(m)) return puts("1 1"), 0;
int n2 = Pow(2, n), mul = 1;
for (ll i = 2; i < m; i <<= 1)
cnt += (m - 1) / i;
for (int i = 1; i < m; ++i)
if (!(mul = 1LL * (n2 - i + M) % M * mul % M)) break;
int ny = Pow(Pow(2, M - 2), cnt);
mul = 1LL * mul * ny % M;
int fm = 1LL * Pow(Pow(2, n), m - 1) * ny % M;
printf("%d %d\n", (fm - mul + M) % M, fm);
C 简单的填数 (Unaccepted)
- 写了个剪枝暴搜,结果没有输出a[n],40分都没了。
D 聪聪和可可
-
看这道题我还以为我之前A过,但是只能想起来要跑最短路,然后再算一个猫下一步会到的位置,然后就想不起来了,就写了个Dfs拿了50分。
-
然而考后看了看之前的题,压根没做过...
-
写个记搜就满分了,说实话我写了这么多年的暴搜了,还真想不出来要这样写搜索(可能是由于这是一道概率题吧)
double Dfs(int x, int y) {
double &s = f[x][y];
if (v[x][y]) return s;
v[x][y] = 1;
if (x == y) return s = 0;
x = g[g[x][y]][y];
if (x == y) return s = 1;
s = Dfs(x, y);
for (int i = head[y]; i; i = e[i].next)
s += Dfs(x, e[i].t);
return s = s / (in[y] + 1) + 1;
}
晚间测试7
我真的是太菜了呀 QwQ
我们的叶队Rank2,可真是太巨了,永远的神。
话说代码都没写怎么知道自己哪里不会,细思极恐
A kill
-
打了个暴搜,剪了一点枝,拿了40。
-
可以发现将人和怪按位置排序之后,如果确定了打那几个怪,肯定是前面的人打前面的怪,可以证明这样比前面的人打后面的怪,后面的人打前面的怪更优(如果是两人两怪,总共有6种情况,模拟一下就会发现,然后根据归纳法就可知n个人n个怪也是同样的道理)
-
这样DP一下就可以了
然而我不会DP,只会暴搜 -
还可以发现打的怪一定是一段连续的区间,因为s的位置是固定的,肯定越靠经s的怪打起来越优,所以所有选的怪的是趋近于s点的
-
那就直接枚举第一个被打的怪是谁就知道了所有要打的怪,然后就好算了。
B 乌鸦喝水 (Unaccepted)
-
纯暴力50分,
-
考试和rui坐一起,导致我都不想思考题目了,
甩锅甩锅,知道这个水缸已经没水了以后就用不上了,没想起来用链表优化,能优化到95!。
联赛模拟测试16
- 论如何在hzoi水到第12
A 阴阳 (Unaccepted)
- 暴搜30分。
B 简单的序列
-
记搜30分
-
最简单的一道题,可我是两个机房里最后一个A掉的 QwQ
-
这题之前的学长都用的DP,然后我们就用卡特兰数了
-
考场上想出来个卡特兰数的写法,可惜有的地方算重了,然后就放弃了
-
改的时候可真是费劲,推了一大堆边界,都没推到主要的上面去
-
j的最小值是 (i+l)/2 向上取整。
for (int i = 0; i <= n - m; ++i)//i:p序列的长度
for (int j = (i + l + 1) / 2; j <= i; ++j)//j:p序列'('的数量
if ((ans += 1LL * Cat(j, i - j) * Cat((n - m - l + r) / 2 - i + j, (n - m + l - r) / 2 - j) % M) >= M)
ans -= M;
C 简单的期望 (Unaccepted)
- 暴搜20分
D 简单的操作
-
直接输出样例10分。
-
考场上也发现了奇环就不能合并成链,因为会出现三个点的环,这样的环无论怎样合并都不能合并成链,然后偶链要合并,弄出一颗树来,求出直径就是答案,然后再把所有联通块答案加起来。
-
可惜是我不会判环,然后偶环也不知道怎么合并,鼓捣了1小时没搞出来,只能放弃
-
正解是先二分图染色把奇环的情况判掉,然后对于偶环,其实就是最短路,便权都是1,然后跑n遍BFS,最后不同联通块内最长的最短路加起来就是合并之后最长链长度。
Code
#include <queue>
#include <cstdio>
#include <cstring>
const int N = 1e3 + 5, M = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int next, t;
}e[M<<1];
int head[N], edc;
void Add(int x, int y) {
e[++edc] = (Edge) {head[x], y};
head[x] = edc;
}
std::queue<int> q;
int n, m, d[N], b[N], cnt, c[N], ans;
bool g, v[N];
void Dfs(int x) {
if (g) return;
for (int i = head[x]; i && !g; i = e[i].next) {
int y = e[i].t;
if (!d[y]) d[y] = -d[x], Dfs(y);
else if (d[y] == d[x]) g = 1;
}
}
void Dfs2(int x) {
c[x] = cnt;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].t;
if (c[y]) continue;
Dfs2(y);
}
}
void Bfs(int u) {
memset(v, 0, sizeof(v));
q.push(u);
v[u] = 1;
d[u] = 0;
while (!q.empty()) {
int x = q.front(); q.pop();
b[c[x]] = std::max(b[c[x]], d[x]);
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].t;
if (v[y]) continue;
v[y] = 1;
d[y] = d[x] + 1;
q.push(y);
}
}
}
int main() {
freopen("merge.in", "r", stdin);
freopen("merge.out", "w", stdout);
n = read(); m = read();
while (m--) {
int x = read(), y = read();
Add(x, y); Add(y, x);
}
for (int i = 1; i <= n; ++i) {
if (d[i]) continue;
d[i] = 1; Dfs(i);
if (g) return puts("-1"), 0;
}
for (int i = 1; i <= n; ++i)
if (!c[i]) ++cnt, Dfs2(i);
for (int i = 1; i <= n; ++i)
Bfs(i);
for (int i = 1; i <= cnt; ++i)
ans += b[i];
printf("%d\n", ans);
return 0;
}
晚间测试6
A 山洞 (Unaccepted)
- 开始写了个Dfs,后来写了个60分DP,都没判重,然后大样例过不去,两个错解对拍了一会儿居然都能拍出错来,不得不说对拍是真的管用。
- 后来打表找规律,发现n是偶数,(m+1)/2为奇数的时候答案为0,本来想特判一下,后来一想就剩10分钟了,万一在改错了,再说出题人也不会往里面放0的吧,结果疯狂打脸,有4个点是0,如果特判能多得20分,真的是无话可说。
B beauty
- 这个题我记得前几天讲过,每条边的贡献就是子树内外特殊点数量的较小值,上来就把正解打上了,然后挂了个对拍。
- 如果要构造的话就是找到树的重心(考虑特殊点),然后以重心为根Dfs一遍,最后让x与x+k配对。
联赛模拟测试15
A 游戏
- 这道题好像是疫情时的分享题,不过丝毫想不出来了,然后就瞎写了个贪心,怎么改对拍都不过,最后只好把暴力交上去拿了50分,听他们说数据水,然后我就把错误的贪心交了一下,居然A了,我这的是难受极了。
- 正解类似晚间测试4的T2哪一天她能重回我身边(还没过T_T),把一个武器的两个属性连起来,建一个图,对于每个连通块,如果是树,那一定有一个不能选(n个点,n-1条边,一条边选一个点,显然),就不选最大的那个,其他的图每个点都可以选。
强烈要求加强数据并重评
B 嘟嘟噜
- 裸的约瑟夫问题,可是看着这数十亿的n,只打了个线性求解约瑟夫问题,然后判了一下m=2的情况,拿了40分。
- 原来我连线性约瑟夫问题也不会...
scanf("%d%d", &n, &m);
int x = 0;
for (int i = 1; i <= n; ++i)
x = (x + m) % i;
printf("%d\n", x + 1);
- 然后发现当m小于i时,有的时候是可以不用取模的,直接乘就好了
int x = 0, i = 1, a;
while (1) {
a = (i - x - 1) / (m - 1) + 1;
if (i + a < n) x = (x + m * a) % (i + a);
else {
x = (x + m * (n - i)) % n;
break;
}
i += a;
}
C 天才绅士少女助手克里斯蒂娜
- 一看见这令人头疼的式子就让我很难受,然而我不知道哪根筋抽了,居然推出来个像样的式子,左右两区间合并成一个区间后的值就是
- 把\(x^2,y^2,xy\)的值维护一下就可以线段树维护了。
- 然而我写了个取模优化,调了3个小时没调出错,最后5分钟把取模优化删了,赶紧打包好,拿了我这场唯一的一个Ac,我真的是太难了。
Tree operator + (const Tree &a, const Tree &b) {
Tree c;
c.s = (1LL * a.x * b.y - 2LL * a.xy * b.xy + 1LL * a.y * b.x + a.s + b.s) % M;
if ((c.s += M) >= M) c.s -= M;
c.x = (a.x + b.x) % M;
c.y = (a.y + b.y) % M;
c.xy = (a.xy + b.xy) % M;
return c;
}
D 凤凰院凶真
- 由于T3看出正解怎么写,但始终没有调出来,写这题的时候特别不在状态,然后我就看错题了。本来让求最长上升公共子序列,但我求成了最长公共子序列,然后就愉快的爆0了。
- 正解就是带路径LCIS,O(n2)的,得抽时间好好看看以前的课件了