2025-12-09 hetao1733837的刷题记录
LG4211 [LNOI2014] LCA
原题链接:[LNOI2014] LCA
分析
任何点的 LCA 一定在 z z z 到根的链上,那么,对于修改,直接差分做更加不错,行,he下代码。
正解
cpp
#include <bits/stdc++.h>
#define mod 201314
using namespace std;
const int N = 50005;
struct node1{
int ans1, ans2;
node1(){ ans1 = ans2 = 0; }
}inp1[N];
struct node2{
int num, pos, z;
bool flag;
node2(){ num = pos = z = flag = 0; }
node2(int a, int b, int c, bool d){
num = a;
pos = b;
z = c;
flag = d;
}
bool operator<(const node2 &a) const{
return this -> pos < a.pos;
}
}inp2[N << 1];
bool operator>(const node2 &a, const node2 &b){
return a.pos > b.pos;
}
int n, q;
vector<int> e[N];
int sz[N], fa[N], de[N], son[N], top[N], seq[N], dfn[N];
struct segtree{
int sum[N << 2], tag[N << 2];
void pushup(int p){
sum[p] = (sum[p << 1] + sum[p << 1 | 1]) % mod;
}
void down1(int p, int l, int r, int v){
sum[p] = (sum[p] + 1LL * (r - l + 1) * v) % mod;
if (l < r)
tag[p] = (tag[p] + v) % mod;
}
void down2(int p, int l, int r){
if (tag[p] && l < r){
int mid = (l + r) >> 1;
down1(p << 1, l, mid, tag[p]);
down1(p << 1 | 1, mid + 1, r, tag[p]);
tag[p] = 0;
}
}
int query(int p, int l, int r, int ql, int qr){
if (ql <= l && r <= qr) return sum[p];
down2(p, l, r);
int mid = (l + r) >> 1, ans = 0;
if (ql <= mid) ans = (ans + query(p << 1, l, mid, ql, qr)) % mod;
if (qr > mid) ans = (ans + query(p << 1 | 1, mid + 1, r, ql, qr)) % mod;
return ans;
}
void modify(int p, int l, int r, int ql, int qr, int v){
if (ql <= l && r <= qr){
down1(p, l, r, v);
return;
}
down2(p, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) modify(p << 1, l, mid, ql, qr, v);
if (qr > mid) modify(p << 1 | 1, mid + 1, r, ql, qr, v);
pushup(p);
}
}tr;
int nowl, nowr;
int query_tree(){
return tr.query(1, 1, n, nowl, nowr);
}
void modify_tree(){
tr.modify(1, 1, n, nowl, nowr, 1);
}
void dfs1(int u){
sz[u] = 1;
son[u] = 0;
for (int v : e[u]){
if (v == fa[u]) continue;
fa[v] = u;
de[v] = de[u] + 1;
dfs1(v);
sz[u] += sz[v];
if (sz[v] > sz[son[u]])
son[u] = v;
}
}
void dfs2(int u, int tp){
static int cnt = 0;
top[u] = tp;
seq[u] = ++cnt;
dfn[cnt] = u;
if (son[u]){
dfs2(son[u], tp);
for (int v : e[u]){
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
}
int query_chain(int x, int y){
int ans = 0;
while (top[x] != top[y]){
if (de[top[x]] < de[top[y]]) swap(x, y);
nowl = seq[top[x]];
nowr = seq[x];
ans = (ans + query_tree()) % mod;
x = fa[top[x]];
}
if (de[x] > de[y]) swap(x, y);
nowl = seq[x];
nowr = seq[y];
ans = (ans + query_tree()) % mod;
return ans;
}
void modify_chain(int x, int y){
while (top[x] != top[y]){
if (de[top[x]] < de[top[y]]) swap(x, y);
nowl = seq[top[x]];
nowr = seq[x];
modify_tree();
x = fa[top[x]];
}
if (de[x] > de[y]) swap(x, y);
nowl = seq[x];
nowr = seq[y];
modify_tree();
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
for (int i = 2; i <= n; i++){
int z;
cin >> z;
z++;
e[z].push_back(i);
e[i].push_back(z);
}
int cnt = 0;
for (int i = 1; i <= q; i++){
int l, r, z;
cin >> l >> r >> z;
l++; r++; z++;
inp2[++cnt] = node2(i, l - 1, z, 0);
inp2[++cnt] = node2(i, r, z, 1);
}
de[1] = 1;
dfs1(1);
dfs2(1, 1);
sort(inp2 + 1, inp2 + cnt + 1);
int now = 0;
for (int i = 1; i <= cnt; i++){
while (now < inp2[i].pos){
modify_chain(1, ++now);
}
int idx = inp2[i].num;
if (inp2[i].flag)
inp1[idx].ans1 = query_chain(1, inp2[i].z);
else
inp1[idx].ans2 = query_chain(1, inp2[i].z);
}
for (int i = 1; i <= q; i++){
cout << (inp1[i].ans1 - inp1[i].ans2 + mod) % mod << '\n';
}
}
CF733F Drivers Dissatisfaction
原题链接1:F.Drivers Dissatisfaction
原题链接2:CF733F Drivers Dissatisfaction
分析
分层图+最小生成树?这不得吃一大坨?
事实证明,我们看得太片面了,怎么办呢?也就是说,从整体上考虑,我们求出来的最小生成树后,用 S S S 的代价减的是最小的花费,这样整体最优。
那我们求所有生成树不就行了?时间爆爆爆!
这个就是这题事紫题的关键了:我们在跑 K r u s k a l Kruskal Kruskal 的过程中,对于一条新加入的边,会成环,那么找出环中最大值删除即可。
对于这部操作,求 L C A LCA LCA + 倍增区间最值即可。
时间复杂度 O ( n l o g 2 n + m l o g 2 n ) O(nlog_2n+mlog_2n) O(nlog2n+mlog2n)。
行,我写完再吃饭。
所以,这题和树剖唯一的关系是求 LCA,以方便将树上问题转化为区间,方便 ST 表求最值?
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
const int M = N << 1;
int i, j, k, m, n, s, t, cnt;
struct Edge{
int nxt, p, val, c, u, v, rk;
} a[M];
struct Tree{
int mx, rk;
} val[N];
int head[N], fa[N], tdv[N], tdc[N];
int rak[N], dfn[N], top[N], son[N], siz[N], dep[N];
int rt[M];
struct SegTree{
Tree T[N << 2];
Tree push_up(Tree aa, Tree bb){
Tree ret;
if (aa.mx > bb.mx){
ret.mx = aa.mx;
ret.rk = aa.rk;
}
else{
ret.mx = bb.mx;
ret.rk = bb.rk;
}
return ret;
}
void build(int l, int r, int rt, int rak[]){
if (l == r) {
T[rt] = val[rak[l]];
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1, rak);
build(mid + 1, r, rt << 1 | 1, rak);
T[rt] = push_up(T[rt<<1], T[rt<<1|1]);
}
Tree query(int l, int r, int rt, int x, int y){
if (x <= l && y >= r)
return T[rt];
Tree ret;
ret.mx = 0;
ret.rk = 0;
if (x > y) return ret;
int mid = (l + r) >> 1;
if (x <= mid)
ret = query(l, mid, rt << 1, x, y);
if (y > mid) {
Tree right_res = query(mid + 1, r, rt << 1 | 1, x, y);
ret = push_up(ret, right_res);
}
return ret;
}
}Tr;
void addEdge(int aa, int bb, int cc, int dd){
a[++k].nxt = head[aa];
head[aa] = k;
a[k].v = bb;
a[k].u = aa;
a[k].val = cc;
a[k].c = dd;
a[k].rk = k;
}
int find(int x){
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool cmp1(Edge aa, Edge bb){
if (aa.val != bb.val)
return aa.val < bb.val;
return aa.rk < bb.rk;
}
bool cmp2(Edge aa, Edge bb){
return aa.rk < bb.rk;
}
int kruskal(){
sort(a + 1, a + k + 1, cmp1);
int ret = 0;
memset(rt, 0, sizeof(int) * (k + 5));
for (i = 1; i <= k; i++)
rt[a[i].rk] = i;
for (i = 1; i <= n; i++)
head[i] = rt[head[i]];
for (i = 1; i <= k; i++)
a[i].nxt = rt[a[i].nxt];
for (i = 1; i <= n; i++)
fa[i] = i;
cnt = 0;
for (i = 1; i <= k; i += 2){
int u = find(a[i].u);
int v = find(a[i].v);
if (u == v)
continue;
ret += a[i].val;
fa[u] = v;
a[i].p = a[i + 1].p = 1;
cnt++;
}
for (i = 1; i <= k; i++)
a[i].rk = (a[i].rk - 1) / 2 + 1;
return ret;
}
void dfs1(int u){
siz[u] = 1;
for (int i = head[u]; i; i = a[i].nxt){
int v = a[i].v;
if (a[i].p == 0) continue;
if (v == fa[u]) continue;
fa[v] = u;
val[v].mx = a[i].val;
val[v].rk = a[i].rk;
dep[v] = dep[u] + 1;
dfs1(v);
siz[u] += siz[v];
if (siz[v] > siz[son[u]])
son[u] = v;
}
}
void dfs2(int u){
dfn[u] = ++t;
rak[t] = u;
if (son[fa[u]] == u)
top[u] = top[fa[u]];
else
top[u] = u;
if (son[u]) dfs2(son[u]);
for (int i = head[u]; i; i = a[i].nxt){
int v = a[i].v;
if (a[i].p == 0) continue;
if (v == fa[u]) continue;
if (v == son[u]) continue;
dfs2(v);
}
}
Tree queryPath(int u, int v){
Tree res;
res.mx = 0;
res.rk = 0;
while (top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v);
Tree tmp = Tr.query(1, n, 1, dfn[top[u]], dfn[u]);
res = Tr.push_up(res, tmp);
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
if (u != v) {
Tree tmp = Tr.query(1, n, 1, dfn[u] + 1, dfn[v]);
res = Tr.push_up(res, tmp);
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (i = 1; i <= m; i++)
cin >> tdv[i];
for (i = 1; i <= m; i++)
cin >> tdc[i];
k = 0;
for (i = 1; i <= m; i++){
int x, y;
cin >> x >> y;
addEdge(x, y, tdv[i], tdc[i]);
addEdge(y, x, tdv[i], tdc[i]);
}
cin >> s;
int all = kruskal();
memset(fa, 0, sizeof(fa));
memset(son, 0, sizeof(son));
dep[1] = 1;
dfs1(1);
dfs2(1);
Tr.build(1, n, 1, rak);
int ans = all, mx1 = 0, mx2 = 0;
for (i = 1; i <= k; i += 2){
if (a[i].p) {
int tmp = all - s / a[i].c;
if (ans > tmp) {
ans = tmp;
mx1 = mx2 = a[i].rk;
}
continue;
}
int u = a[i].u, v = a[i].v;
Tree sum = queryPath(u, v);
if (sum.mx == 0) continue;
int tmp = all + a[i].val - sum.mx - s / a[i].c;
if (tmp < ans){
mx1 = a[i].rk;
mx2 = sum.rk;
ans = tmp;
}
}
cout << ans << '\n';
sort(a + 1, a + k + 1, cmp2);
for (i = 1; i <= k; i += 2){
if ((a[i].p && mx2 != a[i].rk) || mx1 == a[i].rk){
cout << a[i].rk << " ";
int x = a[i].val;
if (mx1 == a[i].rk)
x -= s / a[i].c;
cout << x << '\n';
}
}
return 0;
}
我真的好难受!
LG4292 [WC2010] 重建计划
原题链接:[WC2010] 重建计划
分析
我真的崩溃了/(ㄒoㄒ)/~~
长剖,行。
难道是最小生成树上找一条路径?咋是长剖优化 DP 啊!我为啥啥都不会了!
行,先想暴力 DP,设 f i , j f_{i,j} fi,j 表示以 i i i 为根的子树,往下走 j j j 条边的最大权值和。
与深度有密切的关系,行,长剖优化 DP 底层那就这么整。同样开线段树。再套二分答案。双 log 还是很优秀的。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
const double eps = 1e-5;
const double INF = -1e18;
int a[N], nxt[N], w[N], weight[N];
int dep[N], son[N], pos[N], n, L, R, cnt;
int to[N];
double p, f[N], ans, g[N];
int tot;
void add(int u, int v, int val){
nxt[++tot] = a[u];
to[tot] = v;
w[tot] = val;
a[u] = tot;
}
struct segtree{
double t[N];
void build(int p, int l, int r){
t[p] = INF;
if (l == r)
return ;
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
}
void modify(int p, int l, int r, int s, double val){
t[p] = max(t[p], val);
if (l == r)
return ;
int mid = (l + r) >> 1;
if(s <= mid)
modify(p << 1, l, mid, s, val);
else
modify(p << 1 | 1, mid + 1, r, s, val);
}
double query(int p, int l, int r, int s, int t){
if(l > t || r < s)
return INF;
if(s <= l && r <= t)
return this -> t[p];
int mid = (l + r) >> 1;
return max(query(p << 1, l, mid, s, t), query(p << 1 | 1, mid + 1, r, s, t));
}
}T;
void dfs1(int u, int fa, int val){
dep[u] = -1;
for(int i = a[u]; i; i = nxt[i]){
int v = to[i];
if(v == fa)
continue;
dfs1(v, u, w[i]);
if(dep[u] < dep[v]){
dep[u] = dep[v];
son[u] = v;
weight[u] = w[i];
}
}
dep[u]++;
}
void dfs2(int u, int fa){
if(!pos[u])
pos[u] = ++cnt;
int pu = pos[u];
g[pu] = f[pu] = 0;
if(son[u]){
dfs2(son[u], u);
g[pu] += g[pu + 1] + weight[u] - p;
f[pu] = -g[pu];
}
T.modify(1, 1, n, pu, f[pu]);
for(int i = a[u]; i; i = nxt[i]){
int v = to[i];
if(v == fa || v == son[u])
continue;
dfs2(v, u);
int pv = pos[v];
for(int j = 1; j <= dep[v] + 1; j++){
if(L - j <= dep[u]){
double tmp = T.query(1, 1, n, pu + max(1, L - j), pu + min(R - j, dep[u]));
ans = max(ans, w[i] - p + f[pv + j - 1] + g[pv] + g[pu] + tmp);
}
}
for(int j = 1; j <= dep[v] + 1; j++){
if(w[i] - p + f[pv + j - 1] + g[pv] > g[pu] + f[pu + j]){
f[pu + j] = w[i] - p + f[pv + j - 1] + g[pv] - g[pu];
T.modify(1, 1, n, pu + j, f[pu + j]);
}
}
}
if(dep[u] >= L){
ans = max(ans, g[pu] + T.query(1, 1, n, pu + L, pu + min(R, dep[u])));
}
}
bool check(double x){
T.build(1, 1, n);
p = x;
ans = INF;
dfs2(1, 0);
return ans >= 0;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> L >> R;
int tp = 0;
for(int i = 1; i < n; i++){
int u, v, val;
cin >> u >> v >> val;
add(u, v, val);
add(v, u, val);
}
dfs1(1, 0, 0);
double l = 0, r = 1000000;
while(r - l > eps){
double mid = (l + r) / 2;
if(check(mid))
l = mid;
else
r = mid;
}
cout << fixed << setprecision(3) << l;
return 0;
}
行,启动点分治!
LG3806 【模板】点分治
原题链接:【模板】点分治
分析
就是不断找重心,分治,那么,若两个子树可以组合出 k k k,那就赢了,用一个 j u d g e judge judge 记录所有得出的 d i s dis dis,若 j u d g e k − d i s judge_{k-dis} judgek−dis 也存在,那就赢了,否则输了。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
const int K = 100000005;
int n, m, u, v, w, k;
vector<pair<int, int>> e[N];
bool judge[K], vis[N], ans[N];
int tag[N];
int sz[N], d[N], maxn[N];
int sum, rt;
int ask[N], q[N];
void dfs1(int u, int fa){
sz[u] = 1;
maxn[u] = 0;
for (auto tmp : e[u]){
int v = tmp.first;
if (v == fa || vis[v])
continue;
dfs1(v, u);
sz[u] += sz[v];
maxn[u] = max(maxn[u], sz[v]);
}
maxn[u] = max(maxn[u], sum - sz[u]);
if (maxn[u] < maxn[rt])
rt = u;
}
int tot;
void dfs2(int u, int fa){
tag[++tot] = d[u];
for (auto tmp : e[u]) {
int v = tmp.first, weight = tmp.second;
if (v == fa || vis[v])
continue;
d[v] = d[u] + weight;
dfs2(v, u);
}
}
void work(int u){
int p = 0;
for (auto tmp : e[u]){
int v = tmp.first, weight = tmp.second;
if (vis[v])
continue;
tot = 0;
d[v] = weight;
dfs2(v, u);
for (int j = tot; j; j--){
for (int k = 1; k <= m; k++){
if (ask[k] >= tag[j]) {
ans[k] |= judge[ask[k] - tag[j]];
}
}
}
for (int j = tot; j; j--){
q[++p] = tag[j];
judge[tag[j]] = 1;
}
}
for (int i = 1; i <= p; i++){
judge[q[i]] = 0;
}
}
void solve(int u){
vis[u] = 1;
judge[0] = 1;
work(u);
judge[0] = 0;
for (auto tmp : e[u]) {
int v = tmp.first;
if (vis[v])
continue;
sum = sz[v];
rt = 0;
maxn[0] = 0x3f3f3f3f;
dfs1(v, 0);
solve(rt);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i < n; i++){
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
for (int i = 1; i <= m; i++){
cin >> ask[i];
}
maxn[0] = sum = n;
rt = 0;
dfs1(1, 0);
solve(rt);
for (int i = 1; i <= m; i++){
if (ans[i])
cout << "AYE" << '\n';
else
cout << "NAY" << '\n';
}
return 0;
}
并非理解。
LG4178 Tree
原题链接:P4178 Tree
分析
依旧找到中心,然后分别去两棵子树内统计,用双指针统计。
但是出现了重合情况,减去即可。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 40005;
int n, u, v, w, k;
vector<pair<int, int>> e[N];
int rt, sz[N], mx, sum, d[N], q[N], l, r;
bool vis[N];
void getrt(int u, int fa){
sz[u] = 1;
int maxn = 0;
for (auto tmp : e[u]){
int v = tmp.first, w = tmp.second;
if (v == fa || vis[v])
continue;
sz[u] += sz[v];
maxn = max(maxn, sz[v]);
}
maxn = max(maxn, sum - sz[u]);
if (maxn < mx){
mx = maxn;
rt = u;
}
}
void getdis(int u, int fa){
q[++r] = d[u];
for (auto tmp : e[u]){
int v = tmp.first, w = tmp.second;
if (v == fa || vis[v])
continue;
d[v] = d[u] + w;
getdis(v, u);
}
}
int calc(int u, int val){
r = 0;
d[u] = val;
getdis(u, 0);
int sum = 0;
l = 1;
sort(q + 1, q + r + 1);
while (l < r){
if (q[l] + q[r] <= k){
sum += r - l;
l++;
}
else{
r--;
}
}
return sum;
}
long long ans;
void dfs(int u){
ans += calc(u, 0);
vis[u] = 1;
for (auto tmp : e[u]){
int v = tmp.first, w = tmp.second;
if (vis[v])
continue;
ans -= calc(v, w);
sum = sz[u];
mx = 0x3f3f3f3f3f;
getrt(v, 0);
dfs(rt);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i < n; i++){
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
cin >> k;
sum = n;
mx = 0x3f3f3f3f;
getrt(1, 0);
dfs(rt);
cout << ans;
}
LG4149 [IOI 2011] Race
原题链接:[IOI 2011] Race
分析
我知道是点分治啊?怎么写啊?
树上背包?我赌时间不够!
那么,开一个 m n i mn_i mni 表示权值和为 i i i 的最小边数,再开一个桶统计距离。答案统计比较显然 a n s = min ( a n s , c n t v + m n [ k − d [ v ] ] ) ans=\min(ans, cnt_v+mn[k-d[v]]) ans=min(ans,cntv+mn[k−d[v]])。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005, K = 1000005;
int n, k, rt, sz[N], mx, mn[K], q1[N], q2[N], sum, l, r;
bool vis[N];
vector<pair<int, int>> e[N];
int ans = 0x3f3f3f3f;
void getrt(int u, int fa){
sz[u] = 1;
int maxn = 0;
for (auto tmp : e[u]){
int v = tmp.first;
if (v == fa || vis[v])
continue;
getrt(v, u);
sz[u] += sz[v];
maxn = max(maxn, sz[v]);
}
maxn = max(maxn, sum - sz[u]);
if (maxn < mx){
mx = maxn;
rt = u;
}
}
void getid(int u, int fa, int d1, int d2){
if (d1 > k)
return ;
q1[++l] = d1;
q2[l] = d2;
for (auto tmp : e[u]){
int v = tmp.first, w = tmp.second;
if (v == fa || vis[v])
continue;
getid(v, u, d1 + w, d2 + 1);
}
}
void calc(int u){
mn[0] = 0;
l = 0;
for (auto tmp : e[u]){
int v = tmp.first, w = tmp.second;
if (vis[v])
continue;
int cnt = l;
getid(v, u, w, 1);
for (int j = cnt + 1; j <= l; j++){
if (k - q1[j] >= 0)
ans = min(ans, q2[j] + mn[k - q1[j]]);
}
for (int j = cnt + 1; j <= l; j++){
mn[q1[j]] = min(q2[j], mn[q1[j]]);
}
}
for (int i = 1; i <= l; i++){
if (q1[i] != 0)
mn[q1[i]] = 1e9;
}
}
void solve(int u){
vis[u] = 1;
calc(u);
for (auto tmp : e[u]){
int v = tmp.first;
if (vis[v])
continue;
sum = sz[v];
rt = 0;
mx = sum + 1;
getrt(v, 0);
solve(rt);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 1; i < n; i++){
int u, v, w;
cin >> u >> v >> w;
u++;
v++;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
sum = n;
mx = n + 1;
rt = 0;
getrt(1, 0);
for (int i = 0; i <= k; i++)
mn[i] = 100000000;
solve(rt);
if (ans >= 100000000)
cout << -1;
else
cout << ans;
return 0;
}
LG10421 [蓝桥杯 2023 国 A] 树上的路径
原题链接:[蓝桥杯 2023 国 A] 树上的路径
分析
我悟了!我那所有不超过 R R R 的减去所有不超过 L − 1 L - 1 L−1 的不就行了?
当真如此?
啥?树状数组?并不需要!我找到了一篇!
就是我说的!
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, L, R, d[N], sz[N], mx[N], sum, rt, cnt[N];
long long pre[N];
bool vis[N];
vector<int> e[N], q1, q2;
long long ans;
void buc_sort(vector<int> &tmp){
int maxn = 0;
for (auto v : tmp){
cnt[v]++;
maxn = max(maxn, v);
}
int pos = 0;
for (int i = 0; i <= maxn; i++){
while (cnt[i]){
tmp[pos++] = i;
cnt[i]--;
}
}
}
void getrt(int u, int fa){
sz[u] = 1;
mx[u] = 0;
for (auto v : e[u]){
if (v == fa || vis[v])
continue;
getrt(v, u);
sz[u] += sz[v];
mx[u] = max(mx[u], sz[v]);
}
mx[u] = max(mx[u], sum - sz[u]);
if (mx[u] < mx[rt]){
rt = u;
}
}
void getdis(int u, int fa){
q1.push_back(d[u]);
for (auto v : e[u]){
if (v == fa || vis[v])
continue;
d[v] = d[u] + 1;
getdis(v, u);
}
}
long long add(vector<int> &q, const int k){
long long cur = 0;
int r = q.size() - 1;
for (int i = 0; i < (int)q.size(); i++){
while (r >= 0 && q[i] + q[r] > k)
r--;
if (r <= i)
break;
cur += pre[r] - pre[i] + 1LL * q[i] * (r - i);
}
return cur;
}
void calc(int u){
q2.clear();
q2.push_back(0);
for (auto v : e[u]){
if (vis[v])
continue;
d[v] = 1;
q1.clear();
getdis(v, u);
buc_sort(q1);
for (int i = 0; i < (int)q1.size(); i++){
pre[i] = q1[i];
if (i >= 1)
pre[i] += pre[i - 1];
}
ans -= add(q1, R) - add(q1, L - 1);
for (int i = 0; i < (int)q1.size(); i++)
q2.push_back(q1[i]);
}
buc_sort(q2);
for (int i = 0; i < (int)q2.size(); i++){
pre[i] = q2[i];
if (i >= 1)
pre[i] += pre[i - 1];
}
ans += add(q2, R) - add(q2, L - 1);
}
void solve(int u){
vis[u] = 1;
calc(u);
for (auto v : e[u]){
if (vis[v])
continue;
sum = sz[v];
rt = 0;
mx[0] = n;
getrt(v, u);
solve(rt);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> L >> R;
for (int u = 2; u <= n; u++){
int v;
cin >> v;
e[u].push_back(v);
e[v].push_back(u);
}
mx[0] = n;
sum = n;
rt = 0;
getrt(1, 0);
solve(rt);
cout << ans;
}
我还是没有充分理解点分治的代码。
LG14635 [NOIP2025] 糖果店 / candy
分析
场上样例6没过,把我创飞了。回过头来看,总体思路似乎很对,但是,既然我们时间是够的,为啥不再扫一遍?那不就没问题了?
正赛考过按众数贪心,按差贪心,还有这次,算是按和贪心吧,以后会不会按平均数贪心?按中位数贪心?按积贪心?按商贪心......都有可能,贪心是基础思想,但是其扩展可以到模拟费用流这样的高深算法。必须严格掌握!
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
int n, m;
int a[N], mn = 0x7f7f7f7f, ans;
signed main(){
// freopen("candy.in", "r", stdin);
// freopen("candy.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++){
int x, y;
cin >> x >> y;
a[i] = x;
mn = min(mn, x + y);
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++){
a[i] += a[i - 1];
}
for (int i = 0; i <= n; i++){
if (m < a[i])
break;
ans = max(ans, i + (m - a[i]) / mn * 2);
}
cout << ans;
}