2026-03-04~03-06 hetao1733837 的刷题记录

2026-03-04~06 hetao1733837 的刷题记录

定了,以后一三五七学新东西,二四六刷 HT 的题单!

03-04

基环树吧!

LGP2607 [ZJOI2008] 骑士

原题链接:[ZJOI2008] 骑士

分析

好的,基础知识初步具备,开!

问号?这么一说,好像确实存在一种巧妙的图论建模......我不会

思考一下,如果我们把某一个骑士和他讨厌的骑士连边,显然会出现一个有向的基环树。

然后呢?说很类似 LGP1352?像在哪?喔!!!太妙了!

那么,把一个骑士讨厌的骑士看成他的父亲......好怪啊......设一个骑士是 a a a,他讨厌的是 b b b,在树上, b b b 作为 a a a 的父亲。连一条 b → a b\rightarrow a b→a 的有向边。则形成基环树森林,套用 LGP1352 即可。

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int val[N];
long long dp[N][2];
struct node{
    int nxt, to;
}e[N << 1];
int head[N << 1], vis[N], n, s, tot, res1, res2, cur;
void add(int x, int y){
    e[tot].to = y;
	e[tot].nxt = head[x];
	head[x] = tot++;
}
void check(int u, int pre){
    vis[u] = 1;
    for (int i = head[u]; ~i; i = e[i].nxt){
        if ((i ^ 1) == pre) 
			continue;
        if (vis[e[i].to]){
            res1 = u;
			res2 = e[i].to;
            cur = i;
            continue;
        }
        check(e[i].to, i);
    }
}
void dfs(int u, int pre){
    dp[u][0] = 0;
    dp[u][1] = val[u];
    for (int i = head[u]; ~i; i = e[i].nxt){
        if ((i ^ 1) == pre) 
			continue;
        if (i == cur || (i ^ 1) == cur)
            continue;
        dfs(e[i].to, i);
        dp[u][1] += dp[e[i].to][0];
        dp[u][0] += max(dp[e[i].to][1], dp[e[i].to][0]);
    }
}
signed main(){
    scanf("%d", &n);
    memset(head, -1, sizeof(head));
	for (int i = 1, w, b; i <= n; i++){
    	scanf("%d%d", &w, &b);
		add(i, b);
		add(b, i);
		val[i] = w;
	}
    long long ans = 0;
    for (int i = 1; i <= n; i++){
        if (vis[i]) 
			continue;
        check(i, -2);
        dfs(res1, -1);
        long long tmp = dp[res1][0];
        dfs(res2, -1);
        tmp = max(tmp, dp[res2][0]);
        ans += tmp; 
    }
    printf("%lld", ans);
}

调红温了......

03-05

LGP5049 [NOIP 2018 提高组] 旅行 加强版

原题链接:[NOIP 2018 提高组] 旅行 加强版

分析

OK,我了解了......就是要么是树,要么是基环树,因为联通且 m ≤ n m\le n m≤n。

使得字典序最小?要不要分讨呢?即分讨树和基环树。不怕麻烦的话,可以这样去思考。

先想一下树,由于他有一个非常好的操作,即退回上一个,所以......呢?哦,新城市才记录,那岂不是一个贪子?

基环树其实应该先按照树来做,然后调整一部分即可?那难道一个 dfs 就解决了?我不信!
bur ,那没有加强的岂不是可以直接 O ( n 2 ) O(n^2) O(n2)? Σ(っ °Д °;)っ

搓了一个错的暴力......

显然,对于一棵纯正的树,直接 d f s dfs dfs 即可,对于基环树,问题在于如何对环进行操作,其实,在环上走的时候,只有当其出边是环上的最大的点,且回溯后的点比他小的时候,我们回溯,反之,和真正的树没有任何区别。

自己写一写代码。

好的,没写基环树的情况下获得 60 p t s 60pts 60pts,数据还是很良心的。

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 500005;
int n, m;
struct node{
    int u, v;
}g[N << 1];
int head[N], tot;
struct edge{
    int nxt, to;
}e[N << 1];
void add(int x, int y){
    e[++tot].to = y;
    e[tot].nxt = head[x];
    head[x] = tot;
}
int ans[N], top;
bool vis[N];
void dfs1(int u){
    vis[u] = true;
    ans[++top] = u;
    for (int i = head[u]; i; i = e[i].nxt){
        int v = e[i].to;
        if (vis[v])
            continue;
        dfs1(v);
    }
}
bool cmp(node a, node b){
    return a.v > b.v;
}
int fa[N];
bool flag, ring[N];
void dfs2(int u, int pa){
    if (flag)
        return ;
    if (fa[u] == 0){
        fa[u] = pa;
    }
    else if (fa[u] != pa){
        while (u != pa){
            ring[pa] = true;
            pa = fa[pa];
        }
        ring[u] = true;
        flag = true;
        return ;
    }
    for (int i = head[u]; i; i = e[i].nxt){
        int v = e[i].to;
        if (v == pa)
            continue;
        dfs2(v, u);
    }
}
bool lst;
int pre_mx;
void dfs3(int u){
    vis[u] = true;
    ans[++top] = u;
    if (ring[u]){
        bool fl = false;
        for (int i = head[u]; i; i = e[i].nxt){
            if (lst)
                break;
            int v = e[i].to;
            if (vis[v])
                continue;
            if (ring[v]){
                i = e[i].nxt;
                while (vis[e[i].to])
                    i = e[i].nxt;
                if (i)
                    pre_mx = e[i].to;
                else if (v > pre_mx){
                    fl = true;
                    lst = true; 
                } 
                break;
            }
        }
        for (int i = head[u]; i; i = e[i].nxt){
            int v = e[i].to;
            if (vis[v])
                continue;
            if (ring[v] && fl)
                continue;
            dfs3(v);
        }
    }
    else{
        for (int i = head[u]; i; i = e[i].nxt){
            int v = e[i].to;
            if (vis[v])
                continue;
            dfs3(v);
        }
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1, u, v; i <= m; i++){
        cin >> u >> v;
        g[i] = {u, v};
        g[i + m] = {v, u};
    }
    sort(g + 1, g + 2 * m + 1, cmp);
    for (int i = 1; i <= (m << 1); i++){
        add(g[i].u, g[i].v);
    }
    if (m == n - 1){
        dfs1(1);
        for (int i = 1; i <= top; i++){
            cout << ans[i] << " ";
        }
        return 0;
    }
    else{
        dfs2(1, 1);
        flag = false;
        pre_mx = 0x3f3f3f3f;
        dfs3(1);
        for (int i = 1; i <= top; i++)
            cout << ans[i] << " ";
    }
}

顺手把 LGP5022 [NOIP 2018 提高组] 旅行 也交了。

LGP2947 [USACO09MAR] Look Up S

原题链接:[USACO09MAR] Look Up S

分析

一眼单调栈!

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, h[N], stk[N];
int ans[N];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++){
        cin >> h[i];
    }
    for (int i = n - 1; i >= 1; i--){
        int j = i + 1;
        while (h[i] >= h[j] && h[j] > 0)
            j = ans[j];
        ans[i] = j;
    }
    for (int i = 1; i <= n; i++){
        cout << ans[i] << '\n';
    }
}

03-06

LGP4381 [IOI 2008] Island

原题链接:[IOI 2008] Island

分析

说实话,场上看到这种题,我大概率想不到基环树,谁家把重要性质写到输入格式里?
OK ,我手模一下样例。

模出来了,能说明什么呢?不如先从树开始思考。其实是森林吧......

难道说......但是怎么会是动态规划呢?看一眼题解吧......

每个点只经过一次,且最大,这不是 直径 吗?糖丸了!

那么,找出环,把环看作根,向下搜出最长链,合并的时候,掏一个单调队列优化即可。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
int n;
struct edge{
    int nxt, to, val;
}e[N << 1];
int head[N], tot;
void add(int x, int y, int z){
    e[++tot].to = y;
    e[tot].nxt = head[x];
    e[tot].val = z;
    head[x] = tot;
}
int cnt, st;
int vis1[N], vis2[N], rnd[N], sum[N];
bool check(int u, int pre){
    if (vis1[u] == 1){
        vis1[u] = 2;
        rnd[++cnt] = u;
        vis2[u] = 1;
        return true;
    }
    vis1[u] = 1;
    for (int i = head[u]; i; i = e[i].nxt){
        int v = e[i].to;
        if (i != ((pre - 1) ^ 1) + 1){
            if (check(v, i)){
                if (vis1[u] != 2){
                    rnd[++cnt] = u;
                    vis2[u] = 1;
                    sum[cnt] = sum[cnt - 1] + e[i].val;
                } 
                else{
                    sum[st - 1] = sum[st] - e[i].val;
                    return false;
                }
                return true;
            }
        }
    }
    return false;
}
int d[N], ans;
void dfs(int u){
    vis2[u] = 1;
    for (int i = head[u]; i; i = e[i].nxt){
        int v = e[i].to;
        if (vis2[v])
            continue;
        dfs(v);
        ans = max(ans, d[u] + d[v] + e[i].val);
        d[u] = max(d[u], d[v] + e[i].val);
    }
}
int dp[N << 1];
int calc(int rt){
    st = cnt + 1;
    int res1 = 0, res2 = 0;
    check(rt, 0);
    for (int i = st; i <= cnt; i++){
        ans = 0;
        dfs(rnd[i]);
        res1 = max(res1, ans);
        dp[i] = d[rnd[i]];
    }
    int len = cnt - st + 1;
    for (int i = st; i <= cnt; i++){
        dp[i + len] = dp[i];
        sum[i + len] = sum[i + len - 1] + (sum[i] - sum[i - 1]);
    }
    deque<int> q;
    for (int i = st; i <= st + 2 * len - 1; i++){
        while (!q.empty() && q.front() <= i - len) 
            q.pop_front();
        if (!q.empty()){
            res2 = max(res2, dp[i] + dp[q.front()] + sum[i] - sum[q.front()]);
        }
        while (!q.empty() && dp[q.back()] - sum[q.back()] <= dp[i] - sum[i])
            q.pop_back();
        
        q.push_back(i);
    }
    return max(res1, res2);
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (int i = 1, v, w; i <= n; i++){
        cin >> v >> w;
        add(i, v, w);
        add(v, i, w);
    }
    int res = 0;
    for (int i = 1; i <= n; i++){
        if (!vis2[i]){
            res += calc(i);
        }
    }
    cout << res;
    return 0;
}
相关推荐
逆境不可逃2 小时前
【从零入门23种设计模式16】行为型之迭代器模式
java·开发语言·数据结构·算法·设计模式·职场和发展·迭代器模式
xiaoye-duck2 小时前
《算法题讲解指南:优选算法-分治-归并》--47.归并排序,48.数组中的逆序对
c++·算法
Darkwanderor2 小时前
图论——最短路问题
c++·算法·图论·最短路
Filotimo_2 小时前
3.4 图
算法·图论
I_LPL2 小时前
day49 代码随想录算法训练营 图论专题2
java·算法·深度优先·图论·广度优先·求职面试
小小unicorn2 小时前
[微服务即时通讯系统]语音子服务的实现与测试
c++·算法·微服务·云原生·架构·xcode
xsyaaaan2 小时前
代码随想录Day53图:Floyd算法精讲_ Astar算法精讲_最短路算法总结篇_图论总结
算法·图论
lihihi2 小时前
P10471 最大异或对 The XOR Largest Pair
算法
小白学大数据2 小时前
Pycharm 断点调试 Scrapy:两种实现方式总结
c++·爬虫·scrapy·pycharm