题意
给定一个有向图
这条路径的权值是这条路径上边的权值之和
给定一个有向图
这条路径的权值是这条路径上边的权值之和
题意
本文讲解用于求解最近公共祖先
首先看最近公共祖先问题
解决这一类问题有多种算法
考虑优化上述的dfs
题意
树上莫队
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef pair<int, int> pii;
vector<pii> G[maxn];
struct query {
int first, second, id;
};
struct edge {
int w, id;
};
vector<query> qs;
vector<pii> ans;
int n, q, len, t, tt, bl;
int el[maxn], er[maxn];
edge L[2 * maxn], R[2 * maxn];
bool vis[maxn];
int cnt[maxn], cnt_block[maxn];
void dfs(int u, int fa, int id, int ww) {
el[u] = ++t;
for (auto e : G[u]) {
int v = e.first, w = e.second;
if (v == fa) continue;
R[t] = edge{w, ++tt};
L[t + 1] = edge{w, tt};
dfs(v, u, tt, w);
}
R[t] = edge{ww, id};
er[u] = ++t;
L[t] = edge{ww, id};
}
void add(int a) {
cnt[a]++;
if (cnt[a] == 1) cnt_block[a / bl]++;
}
void del(int a) {
cnt[a]--;
if (cnt[a] == 0) cnt_block[a / bl]--;
}
// ql + n^2/l min, q - n^2/l^2=0, l = n / sqrt(q)
inline bool cmp(const query& a, const query& b) {
if (a.first / len != b.first / len) return a.first / len < b.first / len;
return a.second < b.second;
}
int main()
{
cin.tie(0)->sync_with_stdio(0);
cin >> n >> q;
len = 2 * n / sqrt(q);
bl = sqrt(maxn);
int u, v, w;
for (int i = 1; i < n; i++) {
cin >> u >> v >> w;
w = min(w, n);
G[u].push_back(pii(v, w));
G[v].push_back(pii(u, w));
vis[i] = false;
}
dfs(1, 0, 0, 100005);
for (int i = 1; i <= q; i++) {
cin >> u >> v;
int a = el[u], b = el[v];
if (a > b) {int tmp = a; a = b; b = tmp;}
qs.push_back(query{a, b, i});
}
// 分块
sort(qs.begin(), qs.end(), cmp);
// 莫队
int l = 1, r = 1;
for (auto p : qs) {
int &ql = p.first, &qr = p.second;
while (l > ql) { // 先尽量延长
if (!vis[L[l].id]) {
add(L[l].w);
} else {
del(L[l].w);
}
vis[L[l].id] = !vis[L[l].id];
l--;
}
while (r < qr) { // 先尽量延长
if (!vis[R[r].id]) {
add(R[r].w);
} else {
del(R[r].w);
}
vis[R[r].id] = !vis[R[r].id];
r++;
}
while (l < ql) {
if (!vis[R[l].id]) {
add(R[l].w);
} else {
del(R[l].w);
}
vis[R[l].id] = !vis[R[l].id];
l++;
}
while (r > qr) {
if (!vis[L[r].id]) {
add(L[r].w);
} else {
del(L[r].w);
}
vis[L[r].id] = !vis[L[r].id];
r--;
}
int i, j;
for (i = 0; i <= 100000 / bl; i++) {
if (cnt_block[i] != bl) break;
}
for (j = i * bl; j < (i + 1) * bl; j++) {
if (cnt[j] == 0) break;
}
ans.push_back(pii(p.id, j));
}
sort(ans.begin(), ans.end());
for (auto p : ans) {
cout << p.second << '\n';
}
return 0;
}
这棵二叉树中包含了所有的非负有理数
左侧有一个点$0/1$
接下来
首先证明
SB树中每个数都满足
分子分母互质 : 即每个非负有理数至多出现一次 , 。
证
首先这对根节点成立
现在假设对$m+m'/n+n'$的构造时刻
而这两个等式都与原等式相同
题很好tag[i] != tag[j]
他们之间就要连边dp[i][t]
维护只使用前$t$条边时dp[i]
维护当前时刻以$i$结尾的路的路径权值最大值dp[i]
和dp[j]
通用求众数离线做法
当只需要找频数超过一半的区间众数的时候pos
数组维护出现位置
还有一种做法是随机化
CF716Div2 D AC代码
#include <bits/stdc++.h>
#define N 300000
using namespace std;
typedef pair<int, int> pii;
int n, m, maxn, a[N + 5];
struct node {
int l, r, mx, aa;
};
node xds[4 * N+5];
vector<int> pos[N+5];
int cnt(int x, int l, int r) {
int ret = upper_bound(pos[x].begin(), pos[x].end(), r) - lower_bound(pos[x].begin(), pos[x].end(), l);
return ret;
}
void build(int id, int l, int r) {
if(l == r) {
xds[id].l = xds[id].r = l;
xds[id].mx = 1;
xds[id].aa = a[l];
return;
}
int mid = (l + r) >> 1;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
xds[id].l = l; xds[id].r = r;
if (cnt(xds[id * 2].aa, l, r) > cnt(xds[id * 2 + 1].aa, l, r)) {
xds[id].mx = cnt(xds[id * 2].aa, l, r);
xds[id].aa= xds[id * 2].aa;
} else {
xds[id].mx = cnt(xds[id * 2 + 1].aa, l, r);
xds[id].aa= xds[id * 2 + 1].aa;
}
}
pii get(int id, int l, int r) {
// (frequency, value)
pii ret, ret2;
int f1 = 0, f2 = 0;
if (l <= xds[id].l && xds[id].r <= r) {
return pii(xds[id].mx, xds[id].aa);
}
int mid = (xds[id].l + xds[id].r) >> 1, rl = max(l, xds[id].l), rr = min(r, xds[id].r);
if (l <= mid) {
ret = get(id * 2, l, r);
f1 = cnt(ret.second, rl, rr);
}
if (r > mid) {
ret2 = get(id * 2 + 1, l, r);
f2 = cnt(ret2.second, rl, rr);
}
if (f1 > f2) {
return pii(f1, ret.second);
} else {
return pii(f2, ret2.second);
}
}
int main() {
int l, r;
cin.sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
pos[a[i]].push_back(i);
}
for (int i = 1; i <= n; i++) {
pos[i].push_back(n + 1);
}
build(1, 1, n);
for (int i = 1; i <= m; i++) {
cin >> l >> r;
pii most = get(1, l, r);
cout << max(1, 2 * most.first - (r - l + 1)) << '\n';
}
return 0;
}
话说线上赛有啥游记好写的…但是作为惯例还是写一个