2026-08-21~23 hetao1733837 的刷题记录

2026-08-21 hetao1733837 的刷题记录

LGP3573 POI 2014 RAJ-Rally

原题链接:POI 2014 RAJ-Rally

分析

由于是一个 有向无环图 ,所以,我们考虑进行 拓扑排序

排序之后,我们发现,对于一条路径 ( x , y ) (x,y) (x,y),必有 x < y x<y x<y,且对于这条路径上的点 z z z,其只可能被经历一次。

我们处理 ( x , y ) (x,y) (x,y) 的最长路,即正反两遍拓扑排序 + + +DP,即为到 x x x 的距离、到 y y y 的距离之和加一。

强制经过一条路径意味着这条路径上的所有点都可以被删除,枚举每条边,线段树维护拓扑区间 max ⁡ \max max。而对于不跨过节点的路径,按照之前 DP 直接查找即可。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005, M = 1000005;
int n, m;
vector<int> e[N], g[N];
int deg[N];
int topo[N], ncnt;
int dise[N], disg[N];
struct segtree{
    int tot;
    struct node{
        int sum, ls, rs;
    }tr[N << 2];
    void pushup(int p){
        tr[p].sum = tr[tr[p].ls].sum + tr[tr[p].rs].sum;
    }
    void modify(int &p, int l, int r, int s, int val){
        if (!p)
            p = ++tot;
        if (l == r){
            tr[p].sum += val;
            return ;
        }
        int mid = (l + r) >> 1;
        if (s <= mid)
            modify(tr[p].ls, l, mid, s, val);
        else
            modify(tr[p].rs, mid + 1, r, s, val);
        pushup(p);
    }
    int query(int p, int l, int r){
        if (l == r){
            return l;
        }
        int mid = (l + r) >> 1;
        if (tr[tr[p].rs].sum)
            return query(tr[p].rs, mid + 1, r);
        else    
            return query(tr[p].ls, l, mid);
    }
}T;
int q[N];
void toposort(){
    int l = 1, r = 0;
    for (int i = 1; i <= n; i++){
        if (!deg[i]){
            topo[++ncnt] = i;
            q[++r] = i;
        }
    }
    while (l <= r){
        int u = q[l++];
        for (auto v : e[u]){
            deg[v]--;
            if (!deg[v]){
                q[++r] = v;
                topo[++ncnt] = v;
            }
        }
    }
}
void getdis(){
    for (int i = 1; i <= n; i++){
        int u = topo[i];
        for (auto v : e[u]){
            dise[v] = max(dise[v], dise[u] + 1);
        }
    }
    for (int i = n; i >= 1; i--){
        int u = topo[i];
        for (auto v : g[u]){
            disg[v] = max(disg[v], disg[u] + 1);
        }
    }
}
int rt;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1, a, b; i <= m; i++){
        cin >> a >> b;
        e[a].push_back(b);
        deg[b]++;
        g[b].push_back(a);
    }
    toposort();
    getdis();
    T.tot = 0;
    int mx, id;
    int ans = 0x3f3f3f3f3f3f3f3f;
    for (int i = 1; i <= n; i++){
        T.modify(rt, 0, n, disg[i], 1);
    }
    for (int i = 1; i <= n; i++){
        int u = topo[i];
        T.modify(rt, 0, n, disg[u], -1);
        for (auto v : g[u]){
            T.modify(rt, 0, n, disg[u] + dise[v] + 1, -1);
        }
        mx = T.query(rt, 0, n);
        if (mx < ans){
            ans = mx;
            id = u;
        }
        for (auto v : e[u]){
            T.modify(rt, 0, n, dise[u] + disg[v] + 1, 1);
        }
        T.modify(rt, 0, n, dise[u], 1);
    }
    cout << id << " " << ans;
}

LGP15038 「chaynOI R2 T3」合并同类项

原题链接:「chaynOI R2 T3」合并同类项

分析

发现每次 fqh 出题都会放一个判断 不劣 的东西。

我们发现如果 x ≤ y x\le y x≤y,那直接合并就行了,所以对 y = min ⁡ ( x , y ) y=\min(x,y) y=min(x,y) 是不劣的(我可能想到了吗?)

然后,我们进行区间 DP,设 d p l , r dp_{l,r} dpl,r,但是,这样是 O ( n 3 ) O(n^3) O(n3) 的。

我们观察到这题对 k k k 进行了取模,且随机生成,所以,我们至多拥有 n k \dfrac{n}{k} kn 块,所以,我们对于那些模数相等的,可以直接从前面转移,所以,我们的复杂度来到了 O ( n 3 k ) O(\frac{n^3}{k}) O(kn3)。

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 6005, K = 10005;
int n, k, x, y;
int a[N];
int dp[N][N];
int sum[N];
vector<int> pos[N];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> k >> x >> y;
    for (int i = 1; i <= n; i++){
        cin >> a[i];
        sum[i] = (sum[i - 1] + a[i]) % k;
    }
    for (int i = 1; i <= n; i++){
        for (int j = i; j <= n; j++){
            if (sum[i - 1] == sum[j]){
                pos[j].push_back(i);
            }
        }
    }
    for (int i = 1; i <= n; i++){
        reverse(pos[i].begin(), pos[i].end());
    }
    for (int l = n; l >= 1; l--){
        dp[l][l] = 0;
        for (int r = l + 1; r <= n; r++){
            dp[l][r] = dp[l][r - 1] + x;
            for (auto v : pos[r]){
                if (v > l)
                    dp[l][r] = min(dp[l][r], dp[l][v - 1] + dp[v][r] + y);
                else
                    break;
            }
            if (sum[r - 1] == sum[l - 1]){
                dp[l][r] = min(dp[l][r], dp[l][r - 1] + y);
            }
        }
    }
    cout << dp[1][n];
}

LGP14648 POI 2025/2026 #1 浏览器 / Przeglądarka internetowa

原题链接:POI 2025/2026 #1 浏览器 / Przeglądarka internetowa

分析

建出来字典树,然后树上 DP,设 d p u , 1 / 0 dp_{u,1/0} dpu,1/0 表示是否会到 u u u 节点的最少操作数。转移显然。

为什么要 DP因为每次 E 都会清空 ,就这么简单......不过贪心似乎也是对的,我就是这么写的,每次分支按照长短贪心。

这是水的/ll

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n;
int ch[N][26], tot;
string s[N];
int f[N][22];
int sz[N], mx[N];
int stk[N], tp;
int de[N];
vector<int> son[N];
int pos[N];
void insert(int x){
    int u = 0;
    for (int i = 0; i < (int)s[x].length(); i++){
        if (!ch[u][s[x][i] - 'a']){
            ch[u][s[x][i] - 'a'] = ++tot;
            de[tot] = de[u] + 1;

        }
        u = ch[u][s[x][i] - 'a'];
    }
    sz[u] = (int)s[x].size();
    mx[u] = sz[u];
    son[u].push_back(x);
    pos[x] = u;
}
void dfs1(int u, int fa){
    f[u][0] = fa;
    for (int i = 1; i < 22; i++){
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for (int i = 0; i < 26; i++){
        if (ch[u][i]){
            dfs1(ch[u][i], u);
            mx[u] = max(mx[u], mx[ch[u][i]]);
        }
    }
}
bool cmp(int x, int y){
    return mx[x] < mx[y];
}
int LCA(int x, int y){
    if (de[x] < de[y])
        swap(x, y);
    for (int i = 19; i >= 0; i--){
        if (de[f[x][i]] >= de[y]){
            x = f[x][i];
        }
    }
    if (x == y)
        return x;
    for (int i = 19; i >= 0; i--){
        if (f[x][i] != f[y][i]){
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
string ans;
void dfs2(int u){
    vector<int> tmp;
    for (int i = 0; i < 26; i++){
        if (ch[u][i]){
            tmp.push_back(ch[u][i]);
        }
    }
    sort(tmp.begin(), tmp.end(), cmp);
    for (auto v : son[u]){
        stk[++tp] = v;
    }
    for (auto v : tmp){
        dfs2(v);
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++){
        cin >> s[i];
        insert(i);
    }
    dfs1(0, 0);
    dfs2(0);
    for (int i = 1; i <= tp; i++){
        if (i == 1){
            ans += s[stk[i]];
            ans += 'E';
        }
        else{
            int lca = LCA(pos[stk[i - 1]], pos[stk[i]]);
            if (de[pos[stk[i]]] <= de[pos[stk[i - 1]]] + de[pos[stk[i]]] - de[lca] * 2 + 1){
                ans += s[stk[i]];
                ans += 'E';
            }
            else{
                ans += 'T';
                for (int j = 1; j <= de[pos[stk[i - 1]]] - de[lca]; j++)
                    ans += 'B';
                for (int j = (int)s[stk[i]].size() - de[pos[stk[i]]] + de[lca]; j < (int)s[stk[i]].length(); j++){
                    ans += s[stk[i]][j];
                }
                ans += 'E';
            }
        }
    }
    cout << (int)ans.length() << '\n';
    cout << ans;
}
相关推荐
CQU_JIAKE2 小时前
8.23【A】
算法
sprlightning2 小时前
LHDC V5 算法深度解析
算法·fft·lhdcv5·imdct·mdct·rice·fac
csdn_aspnet2 小时前
C# 第k个最小元素(K’th Smallest Element)
算法·c#·排序算法
BioRunYiXue2 小时前
RACE技术全攻略:从全长克隆到靶基因验证
java·javascript·网络·人工智能·科技·算法·eclipse
ZPC82103 小时前
joy 及sensor_msgs
算法
Three_ST3 小时前
沐神-动手学习深度学习-习题答案4.4模型选择,欠拟合,过拟合
人工智能·python·深度学习·学习·算法
CIO_Alliance3 小时前
AI深度系列(2)| CNN卷积池化感受野原理:从局部感知到全局视野
人工智能·深度学习·线性代数·算法·计算机视觉·企业ai转型·企业cio联盟
探物 AI3 小时前
机器人清理墙角蜘蛛网,背后需要哪些算法?——从视觉分割到贴墙力控
算法·机器人