2025-12-09 hetao1733837的刷题记录

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

原题链接:[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;
}
相关推荐
长安er5 小时前
LeetCode215/347/295 堆相关理论与题目
java·数据结构·算法·leetcode·
元亓亓亓5 小时前
LeetCode热题100--62. 不同路径--中等
算法·leetcode·职场和发展
小白菜又菜5 小时前
Leetcode 1925. Count Square Sum Triples
算法·leetcode
粉红色回忆6 小时前
用链表实现了简单版本的malloc/free函数
数据结构·c++
登山人在路上6 小时前
Nginx三种会话保持算法对比
算法·哈希算法·散列表
写代码的小球7 小时前
C++计算器(学生版)
c++·算法
AI科技星7 小时前
张祥前统一场论宇宙大统一方程的求导验证
服务器·人工智能·科技·线性代数·算法·生活
k***92167 小时前
【C++】继承和多态扩展学习
java·c++·学习
序属秋秋秋7 小时前
《Linux系统编程之进程控制》【进程等待】
linux·c语言·c++·进程·系统编程·进程控制·进程等待
Fuly10248 小时前
大模型剪枝(Pruning)技术简介
算法·机器学习·剪枝