比赛链接:"蔚来杯"2022牛客暑期多校训练营3
这场一堆神仙知识, 听都没听过 ,回头再补
题目链接:A-Ancestor_
题意:
给出两棵编号 1-n 的树A、B,A B树上每个节点均有一个权值,给出k个关键点的编号 x 1 , … , x n x_1,…,x_n x1,…,xn,问有多少种方案使得去掉恰好一个关键点使得剩余关键点在树A上LCA的权值大于树B上LCA的权值。
题解:
预处理出关键点序列的在树A B上的前缀LCA和后缀LCA,枚举去掉的关键节点并使用前后缀LCA算出剩余节点的LCA比较权值即可。
代码:
#include
#include
// #define int long long 会RE ???
using namespace std;
const int maxn = 2e5 + 5;
int n, k;
int pre1[maxn], deep1[maxn], num1[maxn], son1[maxn]; // 第一次dfs的变量
int dfn1[maxn], top1[maxn], rk1[maxn], id1; // 第二次dfs的时候用到的变量
int val1[maxn], dfnval1[maxn]; // 建树时用dfn序下的val
int pre2[maxn], deep2[maxn], num2[maxn], son2[maxn]; // 第一次dfs的变量
int dfn2[maxn], top2[maxn], rk2[maxn], id2; // 第二次dfs的时候用到的变量
int val2[maxn], dfnval2[maxn]; // 建树时用dfn序下的val
int head2[maxn], cnt2;
int x[maxn], a[maxn], b[maxn];
int anc1, anc2;
int preanc1[maxn], preanc2[maxn];
int last1[maxn], last2[maxn];
struct e {
int to, next;
} edge1[maxn << 1], edge2[maxn << 1];
int head1[maxn], cnt1;
struct segtree {
int l, r;
int val, add;
} t1[maxn << 2];
void init() {
cnt1 = 1, id1 = 0;
memset(head1, -1, sizeof(head1));
for (int i = 0; i < maxn; i++) edge1[i].next = -1;
cnt2 = 1, id2 = 0;
memset(head2, -1, sizeof(head2));
for (int i = 0; i < maxn; i++) edge2[i].next = -1;
}
void addedge1(int u, int v) {
edge1[cnt1] = e{v, head1[u]};
head1[u] = cnt1++;
}
void addedge2(int u, int v) {
edge2[cnt2] = e{v, head2[u]};
head2[u] = cnt2++;
}
void predfs1(int u, int fa, int d) {
deep1[u] = d, num1[u] = 1, pre1[u] = fa;
for (int i = head1[u]; i != -1; i = edge1[i].next) {
int v = edge1[i].to;
if (v == fa) continue;
predfs1(v, u, d + 1);
num1[u] += num1[v]; // 子树的节点数
if (num1[son1[u]] < num1[v]) son1[u] = v; // 重链子树的根节点
}
}
void predfs2(int u, int fa, int d) {
deep2[u] = d, num2[u] = 1, pre2[u] = fa;
for (int i = head2[u]; i != -1; i = edge2[i].next) {
int v = edge2[i].to;
if (v == fa) continue;
predfs2(v, u, d + 1);
num2[u] += num2[v]; // 子树的节点数
if (num2[son2[u]] < num2[v]) son2[u] = v; // 重链子树的根节点
}
}
void dfs1(int u, int t) {
dfn1[u] = ++id1, dfnval1[id1] = val1[u], top1[u] = t, rk1[id1] = u;
if (!son1[u]) return; // 按重链顺序进行dfs
dfs1(son1[u], t);
for (int i = head1[u]; i != -1; i = edge1[i].next) {
int v = edge1[i].to;
if (v == pre1[u] || v == son1[u]) continue;
dfs1(v, v); // 更新链首值top为v
}
}
void dfs2(int u, int t) {
dfn2[u] = ++id2, dfnval2[id2] = val2[u], top2[u] = t, rk2[id2] = u;
if (!son2[u]) return; // 按重链顺序进行dfs
dfs2(son2[u], t);
for (int i = head2[u]; i != -1; i = edge2[i].next) {
int v = edge2[i].to;
if (v == pre2[u] || v == son2[u]) continue;
dfs2(v, v); // 更新链首值top为v
}
}
int lca1(int u, int v) {
while (top1[u] != top1[v]) {
if (deep1[top1[u]] > deep1[top1[v]])
u = pre1[top1[u]];
else
v = pre1[top1[v]];
}
return deep1[u] > deep1[v] ? v : u;
}
int lca2(int u, int v) {
while (top2[u] != top2[v]) {
if (deep2[top2[u]] > deep2[top2[v]])
u = pre2[top2[u]];
else
v = pre2[top2[v]];
}
return deep2[u] > deep2[v] ? v : u;
}
signed main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
init();
scanf("%d%d", &n, &k);
for (int i = 1; i <= k; i++) scanf("%d", &x[i]);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i < n; i++) {
int u;
scanf("%d", &u);
addedge1(u, i + 1);
pre1[i + 1] = u;
}
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1; i < n; i++) {
int u;
scanf("%d", &u);
addedge2(u, i + 1);
pre2[i + 1] = u;
}
predfs1(1, 0, 1);
dfs1(1, 1);
predfs2(1, 0, 1);
dfs2(1, 1);
preanc1[1] = x[1], preanc2[0] = x[0];
for (int i = 2; i <= k; i++) {
preanc1[i] = lca1(preanc1[i - 1], x[i]);
}
preanc2[1] = x[1], preanc2[0] = x[1];
for (int i = 2; i <= k; i++) {
preanc2[i] = lca2(preanc2[i - 1], x[i]);
}
last1[k] = x[k], last1[k + 1] = x[k];
for (int i = k - 1; i >= 1; i--) {
last1[i] = lca1(last1[i + 1], x[i]);
}
last2[k] = x[k], last2[k + 1] = x[k];
for (int i = k - 1; i >= 1; i--) {
last2[i] = lca2(last2[i + 1], x[i]);
}
int ans = 0;
for (int i = 1; i <= k; i++) {
int tmp1, tmp2;
if (i == 1) {
tmp1 = last1[i + 1];
tmp2 = last2[i + 1];
} else if (i == k) {
tmp1 = preanc1[i - 1];
tmp2 = preanc2[i - 1];
} else {
tmp1 = lca1(preanc1[i - 1], last1[i + 1]);
tmp2 = lca2(preanc2[i - 1], last2[i + 1]);
}
// printf("%d %d\n", tmp1, tmp2);
if (a[tmp1] > b[tmp2]) ans++;
}
printf("%d\n", ans);
return 0;
}
题目链接:C-Concatenation_
题意:
有若干个字符串,问如何拼接,可以使得所得到的总串字典序最小
题解:
这题真的很坑,挤牙膏般的优化,在进行 cmp 函数比较时,加上 const 和 & 可以节约不少时间
还有一个点是 stable_sort 它可以保证在比较量相等时,要比较的两个元素相对位置不会改变,而 sort 则不能,前者是归并排序,后者是快排
一般对于复杂的数据采用 stable_sort ,这样节省很多拷贝变量的时间
代码:
#include
using namespace std;
const int maxn = 2e6 + 6;
bool cmp(const string &a, const string &b) { return a + b < b + a; }
string s[maxn];
int n;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++) cin >> s[i];
stable_sort(s, s + n, cmp);
for (int i = 0; i < n; i++) cout << s[i];
return 0;
}
题目链接:H-Hacker
题意:
给出一个文本串 A A A , 和 k k k 个模式串,其长度为 m m m ,这些模式串不同位有不同的权值,现在要求你对这 m m m 个模式串求一个最大匹配值。其具体定义为:若某个模式串的 [ l , r ] [l,r] [l,r] 区间与 A A A 匹配,那么这个区间的值为 ∑ i = l r ω i \sum_{i=l}^{r}\omega_i ∑i=lrωi ,现在要求对于不同的模式串的最大的匹配值。
题解:
其实思路还是挺自然的,对这个 A A A 建立 SAM ,让这些模式串在 SAM 上匹配,假设对于模式串 B i B_i Bi 的第 j j j 个字符,它所匹配的 border 是 [ l , r ] [l,r] [l,r] ,那么就记录下 [ l , r ] [l,r] [l,r] 上权值的最大连续子段和。
关于最大连续字段和,其实现原理是线段树,维护以下四个信息即可:区间最大前缀、区间最大后缀、区间总和、区间最大连续子段和即可。
代码:
#include
#define ll long long
#define ls u << 1
#define rs u << 1 | 1
#define mm(x) memset(x, 0, sizeof(x))
using namespace std;
const int INF = 998244353;
const int P = 1e9 + 7;
const int N = 2e6 + 5;
int T;
int n, m, k;
char s[N];
char t[N];
int tot, las;
int ans;
int pos[N];
int len[N], fa[N], trip[N][26];
bool cmp(int a, int b) { return len[a] < len[b]; }
void extend(int u) {
int p = las;
int np = las = ++tot;
// val[np]=1;
len[np] = len[p] + 1;
for (; p && !trip[p][u]; p = fa[p]) trip[p][u] = np;
if (!p)
fa[np] = 1;
else {
int q = trip[p][u];
if (len[q] == len[p] + 1)
fa[np] = q;
else {
int nq = ++tot;
fa[nq] = fa[q];
for (int i = 0; i < 26; ++i) trip[nq][i] = trip[q][i];
len[nq] = len[p] + 1;
fa[q] = fa[np] = nq;
for (; p && trip[p][u] == q; p = fa[p]) trip[p][u] = nq;
}
}
}
ll val[N];
ll mn[N];
void build(int u, int l, int r) {
if (l == r) {
mn[u] = val[l];
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
mn[u] = min(mn[ls], mn[rs]);
}
ll query(int u, int l, int r, int L, int R) {
if (L <= l && r <= R) return mn[u];
int mid = (l + r) >> 1;
ll res = 1e15;
if (L <= mid) res = min(res, query(ls, l, mid, L, R));
if (R > mid) res = min(res, query(rs, mid + 1, r, L, R));
return res;
}
void solve() {
scanf("%s", s + 1);
ll res = 0;
for (int i = 1, cur = 1, plen = 0; i <= m; ++i) {
int ch = s[i] - 'a';
while (cur && !trip[cur][ch]) cur = fa[cur], plen = len[cur];
if (!cur)
cur = 1;
else {
cur = trip[cur][ch];
plen++;
}
ll tmp = query(1, 0, m, i - plen, i);
res = max(res, val[i] - tmp);
}
printf("%lld\n", res);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d%d%d", &n, &m, &k);
scanf("%s", s + 1);
tot = las = 1;
for (int i = 1; i <= n; ++i) extend(s[i] - 'a');
// for (int i = 1; i <= n; ++i) pos[i] = tot + 1, extend(s[i] - 'a');
for (int i = 1; i <= m; ++i) scanf("%lld", &val[i]);
for (int i = 1; i <= m; ++i) val[i] += val[i - 1];
build(1, 0, m);
for (int i = 1; i <= k; ++i) solve();
return 0;
}
题目链接:J-Journey_
题意:
给出一张城市地图,求从起点走到终点的最短时间
题解:
建图,跑下 dijkstra 即可
代码:
#include
#define pii pair<int, int>
using namespace std;
const int maxn = 5e5 + 5;
const int inf = 0x3f3f3f3f;
int n, s1, s2, t1, t2, dir1, dir2;
struct Node {
int a[5];
} e[maxn];
int dis[5][maxn];
bool vis[5][maxn];
void bfs() {
queue<pii> q;
for (int i = 1; i <= 4; i++)
if (e[s2].a[i] == s1) dir1 = i;
for (int i = 1; i <= 4; i++)
if (e[t2].a[i] == t1) dir2 = i;
q.push(pii(dir1, s2));
vis[dir1][s2] = 1;
dis[dir1][s2] = 0;
while (!q.empty()) {
pii now = q.front();
q.pop();
int start = now.second, tdir = now.first;
vis[tdir][start] = 0;
for (int i = 1; i <= 4; i++) {
int w = 1, v = e[start].a[i], vdir;
if (v == 0) continue;
if (i == tdir % 4 + 1) w = 0;
for (int j = 1; j <= 4; j++)
if (e[v].a[j] == start) vdir = j;
if (dis[vdir][v] > dis[tdir][start] + w) {
dis[vdir][v] = dis[tdir][start] + w;
if (!vis[vdir][v]) q.push(pii(vdir, v)), vis[vdir][v] = 1;
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
memset(dis, 0x3f, sizeof dis);
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= 4; j++) cin >> e[i].a[j];
cin >> s1 >> s2 >> t1 >> t2;
bfs();
if (dis[dir2][t2] != inf)
cout << dis[dir2][t2] << endl;
else
cout << -1 << endl;
return 0;
}
不会做X