JZOJ 2022.07.06【提高组A】模拟

历程

被暴打了
原因是钻进了 T4 的坑中。。。
先看完题,发现 T4 比较有意思,T2 没有想法
T3 挺容易,做法似乎很好想
T1 送分,十几分钟搞定
然后开 T4,推了一下,明白题目需要我们干的事情
开码,发现修改与标记有些烦,然后标记打错了,一直调到 11
疯了!
还有 45 min,只要忍痛弃了
看看 T3,很好做,5min 打完
有充满自信,放弃 T2 部分分,继续调 T4
但始终没有意识到一处标记的问题
导致比赛结束被暴打(更重要的是,暴力每题普遍 8090pts !!神仙数据。。。)

T1 1782. Travel

这不 Floyd 预处理后分层最短路 spfa 啊!
正好复习最短路模板

Code

Code

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#define RE register
using namespace std;
const int N = 105, INF = 0x3f3f3f3f;
int n, m, q, G[N][N], vis[N][N], bz[N][15], f[N][15];
struct node{int x, k;};
queue<node> Q;
void SPFA()
{
memset(f, INF, sizeof f), Q.push(node{1, 0}), f[1][0] = 0, bz[1][0] = 1;
while (!Q.empty())
{
node z = Q.front(); Q.pop();
for(RE int i = 1; i <= n; i++)
if (vis[z.x][i] && i ^ z.x)
{
int j = i, k = z.k, w = G[z.x][i];
if (!vis[i][z.x]) w *= 2, ++k;
if (k <= q && f[j][k] > f[z.x][z.k] + w)
{
f[j][k] = f[z.x][z.k] + w;
if (!bz[j][k]) Q.push(node{j, k}), bz[j][k] = 1;
}
}
bz[z.x][z.k] = 0;
}
}
int main()
{
scanf("%d%d%d", &n, &m, &q), memset(G, INF, sizeof G);
for(RE int i = 1, x, y, z; i <= m; i++)
scanf("%d%d%d", &x, &y, &z), G[x][y] = min(G[x][y], z), vis[x][y] = 1;
for(RE int k = 1; k <= n; k++)
for(RE int i = 1; i <= n; i++)
if (i ^ k)
for(RE int j = 1; j <= n; j++)
if (j ^ i && j ^ k)
vis[i][j] = (vis[i][j] | (vis[i][k] && vis[k][j]));
SPFA();
int ans = INF;
for(RE int i = 1; i <= q; i++) ans = min(ans, f[n][i]);
if (ans == INF) ans = -1;
printf("%d\n", ans);
}

T2 3337. wyl8899的TLE

思考答案的产生
A 的首位, B 的某个位置开始,求 LCP
然后魔法地同时跳过一格(计入贡献),再求 LCP
答案取最大值即可
也就是说可以枚举 B 的开始位置,二分加哈希求 LCP 即可
思维真不高。。。
也算复习字符串基操吧

Code

Code

#include <cstdio>
#include <cstring>
#include <iostream>
#define RE register
#define IN inline
using namespace std;
typedef long long LL;
const int N = 5e4 + 5, P1 = 1e9 + 7, P2 = 1e9 + 9, B = 29;
char s1[N], s2[N];
LL b1[N], b2[N];
struct Hash{
int n; LL p1[N], p2[N];
IN void init(char *s)
{
n = strlen(s + 1);
for(RE int i = 1; i <= n; i++)
p1[i] = (p1[i - 1] * B + s[i] - 'a') % P1, p2[i] = (p2[i - 1] * B + s[i] - 'a') % P2;
}
IN int get1(int l, int r){return (p1[r] - p1[l - 1] * b1[r - l + 1] % P1 + P1) % P1;}
IN int get2(int l, int r){return (p2[r] - p2[l - 1] * b2[r - l + 1] % P2 + P2) % P2;}
}h1, h2;
IN int lcp(int l1, int l2)
{
int l = 0, r = min(h1.n - l1 + 1, h2.n - l2 + 1), mid = l + r >> 1, res = 0;
for(; l <= r; mid = l + r >> 1)
if ((h1.get1(l1, l1 + mid - 1) == h2.get1(l2, l2 + mid - 1))
&& (h1.get2(l1, l1 + mid - 1) == h2.get2(l2, l2 + mid - 1))) res = mid, l = mid + 1;
else r = mid - 1;
return res;
}
int main()
{
b1[0] = b2[0] = 1;
for(RE int i = 1; i <= N - 3; i++) b1[i] = b1[i - 1] * B % P1, b2[i] = b2[i - 1] * B % P2;
scanf("%s%s", s1 + 1, s2 + 1), h1.init(s1), h2.init(s2);
int ans = 0;
for(RE int i = 1, len; i <= h2.n; i++)
ans = max(ans, (len = lcp(1, i)) + lcp(len + 2, i + len + 1) + (len < h1.n && i + len - 1 < h2.n));
printf("%d\n", ans);
}

T3 3338. 法法塔的奖励

考虑一个点的答案,显然由子树符合条件的 dp 值转来
发现是维护子树的问题,所以树上启发式合并加树状数组做到 O(nlog2n) 或者线段树合并做到 O(nlogn)
当然是打前者啦,跟打暴力一样一样的

Code

Code

#include <cstdio>
#include <iostream>
#define RE register
#define IN inline
using namespace std;
const int N = 1e5 + 5;
int n, fa[N], a[N], h[N], tot, c[N];
int dfc, siz[N], dfn[N], rev[N], son[N], ans[N];
struct edge{int to, nxt;}e[N];
IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
void dfs1(int x)
{
siz[x] = 1, dfn[x] = ++dfc, rev[dfc] = x;
for(RE int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
dfs1(v), siz[x] += siz[v];
if (siz[v] > siz[son[x]]) son[x] = v;
}
}
IN int lowbit(int x){return x & (-x);}
IN void update(int x, int v){for(; x <= n; x += lowbit(x)) c[x] = max(c[x], v);}
IN void clear(int x){for(; x <= n; x += lowbit(x)) c[x] = 0;}
IN int query(int x)
{
int res = 0;
for(; x; x -= lowbit(x)) res = max(res, c[x]);
return res;
}
void dfs2(int x, int kp)
{
for(RE int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == son[x]) continue;
dfs2(v, 0);
}
if (son[x]) dfs2(son[x], 1);
for(RE int i = h[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == son[x]){update(a[son[x]], ans[son[x]]); continue;}
for(RE int j = dfn[v]; j <= dfn[v] + siz[v] - 1; j++) update(a[rev[j]], ans[rev[j]]);
}
ans[x] = query(a[x]) + 1;
if (!kp) for(RE int i = dfn[x] + 1; i <= dfn[x] + siz[x] - 1; i++) clear(a[rev[i]]);
}
int main()
{
scanf("%d", &n); int x; scanf("%d", &x);
for(RE int i = 2; i <= n; i++) scanf("%d", &fa[i]), add(fa[i], i);
for(RE int i = 1; i <= n; i++) scanf("%d", &a[i]);
dfs1(1), dfs2(1, 1);
for(RE int i = 1; i <= n; i++) printf("%d ", ans[i]);
}

T4 3339. wyl8899和法法塔的游戏

很容易想到 nim 游戏的结论:异或和为零为先手必败态
那么先求异或和,需要第一步取完后留给对手必败态,即 (arx)others=0
x=arothers 最大化,可持久化 Trie 即可
但是需要支持修改操作,所以考虑分块加 Trie
注意打标记时的细节

Code

Code

#include <cstdio>
#include <cmath>
#include <iostream>
#define IN inline
#define RE register
using namespace std;
const int N = 1e5 + 5, B = 319;
int n, m;
struct DS{
int pos[N], block, a[N], tag[B], tr[N * 20][2], size, sum[N * 20], rt[B];
IN void init()
{
block = sqrt(n);
for(RE int i = 1; i <= n; i++) pos[i] = (i - 1) / block + 1;
}
IN void update(int p, int x, int v)
{
if (!rt[p]) rt[p] = ++size;
int u = rt[p];
for(RE int i = 10; i >= 0; i--)
{
int ch = (x >> i) & 1;
if (!tr[u][ch]) tr[u][ch] = ++size;
sum[u = tr[u][ch]] += v;
}
}
IN int query(int p, int v)
{
int u = rt[p], res = 0;
for(RE int i = 10; i >= 0; i--)
{
int ch = (v >> i) & 1;
if (sum[tr[u][ch]]) u = tr[u][ch];
else if (sum[tr[u][ch ^ 1]]) u = tr[u][ch ^ 1], res |= (1 << i);
}
return res;
}
IN int get(int x){return a[x] ^ tag[pos[x]] ^ a[x + 1] ^ tag[pos[x + 1]];}
IN void Modify(int r, int v)
{
int t = get(r), w = a[r] ^ (t - v) ^ tag[pos[r]] ^ a[r + 1] ^ tag[pos[r + 1]];
for(RE int i = r; i >= (pos[r] - 1) * block + 1; i--)
update(pos[r], a[i], -1), update(pos[r], a[i] ^= w, 1);
for(RE int i = 1; i < pos[r]; i++) tag[i] ^= w;
}
IN int Query(int l, int r, int v)
{
int res = N;
for(RE int i = l; i <= min(pos[l] * block, r); i++) res = min(res, a[i] ^ tag[pos[l]] ^ v);
if (pos[l] ^ pos[r])
for(RE int i = (pos[r] - 1) * block + 1; i <= r; i++) res = min(res, a[i] ^ tag[pos[r]] ^ v);
for(RE int i = pos[l] + 1; i < pos[r]; i++) res = min(res, query(i, v ^ tag[i]));
return res;
}
}T;
int main()
{
scanf("%d", &n);
for(RE int i = 1; i <= n; i++) scanf("%d", &T.a[i]);
T.init();
for(RE int i = n; i; i--) T.a[i] ^= T.a[i + 1], T.update(T.pos[i], T.a[i], 1);
scanf("%d", &m);
for(RE int i = 1, ed, L, R; i <= m; i++)
{
scanf("%d%d%d", &ed, &L, &R);
int ans = T.get(ed) - T.Query(L, R, T.a[ed] ^ T.tag[T.pos[ed]]);
if (ans <= 0) ans = -1;
if (ans != -1) T.Modify(ed, ans);
printf("%d\n", ans);
}
}

可见这套题挺简单的,但是考场要分配好时间,避免陷入一处坑很久导致意外或者“正常”死亡

posted @   leiyuanze  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2021-07-06 Luogu P3601 签到题
2021-07-06 二进制枚举子集
点击右上角即可分享
微信分享提示