首页 > 其他分享 >[POI2014] RAJ-Rally 题解

[POI2014] RAJ-Rally 题解

时间:2024-09-04 21:53:06浏览次数:5  
标签:int 题解 RAJ xym res yzh Rally 最长 dis

前言

题目链接:Hydro & bzoj黑暗爆炸洛谷

题意简述

DAG 求删点后最长路的最小值。

\(n \leq 5 \times 10 ^ 5\),\(m \leq 10^6\)。

题目分析

其实对于删点 / 边加查询最长 / 短路的套路是有的。比如:故乡的梦。本题也类似。

我们考虑,如果删除的边不在原来最长路上,那么删之后的图的最长路不变。又因为我们一定可以删除最长路上的一条边来得到不劣的答案,所以,我们删除就只会删除原来最长路上的某一个点。如果最长路不唯一,任取不影响正确性。

考虑最长路 \(d_1 \ldots d_k\),设删除的点为 \(u = d_{p}\)。那么删除后的最长路可能是 \(d_1 \ldots d_{p - 1}\) 或 \(d_{p + 1} \ldots d_k\),也可能是其他不经过点 \(u\) 的最长路。这么看似乎没什么头绪,我们稍微转化一下。对于原图的任意最长路,其起点入度和终点出度必为 \(0\),所以套路地,我们用一个超级源点 \(S\) 连向所有入度为 \(0\) 的点,所有出度为 \(0\) 的点连向超级汇点 \(T\)。这样,我们发现最长路 \(d_1 = S\),\(d_k = T\)。以及,删点后的最长路一定形如:\(S \rightarrow d_{i} \rightarrow x \rightarrow y \rightarrow d_{j} \rightarrow T\),其中 \(i \lt p\) 并且 \(j \gt p\)。那么 \(d_i\) 就是以 \(S\) 为根的最长路径树上,\(x\) 到链 \(S \sim T\) 的结点,\(d_j\) 是以 \(T\) 为根的反图的最长路径树上, \(y\) 到链 \(S \sim T\) 的结点。这是由于,我们需要使得 \(S \rightarrow x\) 和 \(y \rightarrow T\) 是最长路。

用 BFS 或 DFS 什么的标记一下预处理很方便。不妨把 \((x, y)\) 这条非树边挂在 \(d_i\) 上,记作二元组 \(v_i = (d_j, len)\),其中 \(len\) 是经过 \((x, y)\) 最长路长度。查询变成了在 \(v_{1 \sim p - 1}\) 中,前者 \(\gt p\) 的后者的最小值。

按照 \(p = 1 \ldots k\) 的顺序扫过去,对于一个 \(d_i = p\) 的二元组,在一个数据结构的 \(d_j\) 位置取 \(\max \{ len \}\)。对于 \(p \in [2, k - 1]\),我们查询删除这个点的答案,就是在 \(i - 1\) 操作后的基础上查询 \(i + 1 \sim k\) 的最大值。树状数组维护即可。

时间复杂度:\(\Theta((n + m) \log n)\),瓶颈在于树状数组单点修改,后缀查最值。

代码

#include <cstdio>
#include <iostream>
using namespace std;

constexpr const int MAX = 1 << 27, yzh_i_love_you = 1314520736;
char buf[MAX], *p = buf;
#define getchar() (*p++)
#define isdigit(x) ('0' <= x && x <= '9')
inline void read(int &x) {
    x = 0; char c = 0;
    for (;!isdigit(c); c = getchar());
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
}

const int N = 500010;
const int M = 1000010;

int n, m;

int line[N], whr[N], tim;
bool inpath[M];

struct Graph {
    struct node {
        int to, nxt;
    } edge[M + N * 2];
    int head[N], du[N], tot;
    inline void add(int u, int v) {
        edge[++tot] = {v, head[u]};
        head[u] = tot, ++du[v];
    }
    inline node & operator [] (int x) {
        return edge[x];
    }
    int dis[N], fr[N], bl[N];
    inline void solve(int root) {
        static int Q[N], top(0);
        Q[++top] = root, dis[root] = -1;
        while (top) {
            int now = Q[top--];
            for (int i = head[now]; i; i = edge[i].nxt) {
                int to = edge[i].to;
                if (dis[now] + 1 >= dis[to]) {
                    dis[to] = dis[now] + 1;
                    fr[to] = i;
                }
                if (!--du[to]) Q[++top] = to;
            }
        }
    }
    void mark(int s) {
        static int Q[N], top(0);
        Q[++top] = s;
        bl[s] = s;
        while (top) {
            int now = Q[top--];
            for (int i = head[now]; i; i = edge[i].nxt) {
                int to = edge[i].to;
                if (fr[to] != i) continue;
                bl[to] = inpath[i] ? to : bl[now];
                Q[++top] = to;
            }
        }
    }
} xym, yzh;

struct Trans {
    struct node {
        int to, len, nxt;
    } edge[M];
    int tot, head[N];
    inline void add(int u, int v, int w) {
        edge[++tot] = {v, w, head[u]};
        head[u] = tot;
    }
    inline node & operator [] (int x) {
        return edge[x];
    }
} trans;

void getpath() {
    int T = 1, S;
    for (int i = 1; i <= n; ++i) if (xym.dis[i] > xym.dis[T]) T = i;
    for (S = T; xym.fr[S]; S = yzh[xym.fr[S]].to);
    for (int i = S; ; i = xym[yzh.fr[i]].to) {
        line[++tim] = i;
        whr[i] = tim;
        if (!yzh.fr[i]) break;
        inpath[yzh.fr[i]] = true;
    }
}

struct Bit_Tree {
    int tree[N];
    inline int lowbit(int x) {
        return x & -x;
    }
    inline void modify(int p, int v) {
        for (int i = p; i; i -= lowbit(i)) tree[i] = max(tree[i], v);
    }
    inline int query(int p) {
        int res = 0;
        for (int i = p; i <= tim; i += lowbit(i)) res = max(res, tree[i]);
        return res;
    }
} tree;

signed main() {
    fread(buf, 1, MAX, stdin);
    read(n), read(m);
    for (int i = 1, u, v; i <= m; ++i) {
        read(u), read(v);
        xym.add(u, v);
        yzh.add(v, u);
    }
    for (int i = 1; i <= n; ++i) {
        if (!xym.du[i]) xym.add(0, i), yzh.add(i, 0);
        if (!yzh.du[i]) xym.add(i, n + 1), yzh.add(n + 1, i);
    }
    xym.solve(0), yzh.solve(n + 1);
    getpath();
    xym.mark(0), yzh.mark(n + 1);
    for (int u = 1; u <= n; ++u)
        for (int i = xym.head[u]; i; i = xym[i].nxt) {
            int v = xym[i].to;
            if (xym.fr[v] == i) continue;
            if (whr[xym.bl[u]] + 1 >= whr[yzh.bl[v]]) continue;
            trans.add(whr[xym.bl[u]], whr[yzh.bl[v]], xym.dis[u] + 1 + yzh.dis[v]);
        }
    int ans = 0x3f3f3f3f, u = -1;
    for (int i = 1; i <= tim - 1; ++i) {
        if (i >= 2) {
            int res = tree.query(i + 1);
            res = max(res, xym.dis[line[i - 1]]);
            res = max(res, yzh.dis[line[i + 1]]);
            if (res < ans) ans = res, u = line[i];
        }
        for (int j = trans.head[i]; j; j = trans[j].nxt)
            tree.modify(trans[j].to, trans[j].len);
	}
    printf("%d %d", u, ans);
    return 0;
}

标签:int,题解,RAJ,xym,res,yzh,Rally,最长,dis
From: https://www.cnblogs.com/XuYueming/p/18397392

相关文章

  • CF1941场题解
    RudolfandtheTicket算法:枚举。题意简述:从\(a\)数组中和\(b\)数组中各选出一个数,使得它们的和不超过\(k\),求选法数量。考虑直接枚举每一种可能的搭配即可。Rudolfand121算法:贪心。题意简述:定义一次操作为,该位置上的数减去\(2\),其前一个和后一个位置(必须存在)上的数......
  • 2023 ICPC 合肥题解
    gymD.BalancedArray\(\star\)赛时做法枚举前缀维护合法的\(k\)感性上\(k\)越大需要满足的式子越少,只保留最大的\(\log\)个\(k\),可以通过std枚举\(k\),合法的\(l\)一定是一个左端点为\(2k+1\)的区间,二分右端点等式\(\forall1\lei\lel-2k,a_{i}+a_{i+2k}=2a......
  • Codeforces Round 971 (Div. 4) ABCD题详细题解(C++,Python)
    前言:    本文为CodeforcesRound971(Div.4)ABCD题的题解,包含C++,Python语言描述,觉得有帮助或者写的不错可以点个赞    比赛打了没一半突然unrated了就不是很想继续写了,早起写个题解    (之前的div3也没复盘,哎真菜)目录题A:题目大意和解题......
  • AtCoder Beginner Contest 369 题ABCD详细题解--包含解题思路和两种语言(C++,Python)
    前言:    本文为AtCoderBeginnerContest369题ABCD详细题解,包括题目大意,详细的解题思路和两种语言描述,觉的有帮助或者写的不错可以点个赞几天前的比赛拖的有点久了比赛题目连接:Tasks-AtCoderBeginnerContest369目录题A:题目大意和解题思路:代码(C++):......
  • 洛谷 B3645 数列前缀和 2 题解
    前缀知识:枚举,费马小定理,逆元,线性乘法逆元,线段树(?)。解法1:暴力如题。暴力枚举即可,30分。由于太简单,不给代码。解法2:前缀积+费马小定理+逆元由于涉及静态区间,可以想到前缀积。前缀积公式为\(q_r/q_{l-1}\),除法恰好可以用逆元来算。直接写即可。不会超时,因为时间为\(O(n\logp)\)......
  • Codeforces Round 971 (Div. 4) E 题解析
    #E题Klee'sSUPERDUPERLARGEArray!!!题目描述思路:对于这道题,首先观察到题目求的是最小可能值,而且数据的范围是1e9范围,所以首先可以考虑的方法就是O(logn)的方法,而适合求最值的方法无疑就是二分搜索,可以通过二分来不断缩小答案的区间......
  • 常见问题解决 --- 如何给一个不支持配置代理的程序抓取https流量数据
    比如我有一个C#编写票务系统,它内嵌浏览器功能,我想抓取它的流量,但是这个客户端不支持配置代理设置解决办法:1.安装配置proxifier开启全局代理服务。安装好后网上有激活码激活一下,点击profile-proxyserver,添加一个代理服务器127.0.0.1,端口8080,协议https。点击profile-proxifi......
  • 历年CSP-J初赛真题解析 | 2017年CSP-J初赛阅读程序(23-26)
    学习C++从娃娃抓起!记录下CSP-J备考学习过程中的题目,记录每一个瞬间。附上汇总贴:历年CSP-J初赛真题解析|汇总_热爱编程的通信人的博客-CSDN博客#include<iostream>usingnamespacestd;intmain(){ intt[256]; strings; inti; cin>>s; for(i=0;i<256;i......
  • 2024年“羊城杯”粤港澳大湾区网络安全大赛 初赛 Web&数据安全&AI 题解WriteUp
    文章首发于【先知社区】:https://xz.aliyun.com/t/15442LyricsForYou题目描述:Ihavewrotesomelyricsforyou…开题。看一下前端源码,猜测有路径穿越漏洞http://139.155.126.78:35502/lyrics?lyrics=../../../../../etc/passwd简单看一下环境变量,没有flag。扫......
  • 中国电子学会Python3级等级考试202403编程题解析1
    1编程题目整数问题给定一个十进制整数n,求出从1到n的所有整数中出现“1”的个数。例如,n=2时,1,2出现1个“1”。n=12时,1,2,3,4,5,6,7,8,9,10,11,12,出现5个“1”。现编写一个程序,实现如下功能:输入整数n,执行程序后,输出该范围内出现“1”的个数。请完善程序。图1要完善的程序......