P3573 [POI2014]RAJ-Rally

Problem

给一个\(n\)个点,\(m\)条边的DAG,找到一个点,使得删去这个点后的最长路径最短。
\(2 \le n \le 500000,1 \le m \le 1000000\)

Solution

看到DAG就想到拓扑。
求出以\(x\)为起点的最长路径长度\(ds_x\)和以\(x\)为终点的最长路径长度\(dt_x\)。可以通过跑正反图拓扑实现。
设第\(x\)个点的拓扑序为\(TX_x\)
对于一条边\(u \to v\),经过它的最长路径长度为\(dt_u + ds_v + 1\),正确性显然。
考虑枚举删的点,假设我们现在删除\(x\),我们假设所有\(TX_i < TX_x\)的点放在集合\(A\)\(TX_i > TX_x\)的放在集合\(B\)\(x\)在中间。
此时的最长路径分三种:\(A\to A,A \to B,B\to B\)

  • \(A\to A\)\(\max_{i \in A} \{dt_i\}\)
  • \(A \to B\)\(\max_{(u,v) \in E,u \in A,v \in B} \{dt_u +ds_v + 1 \}\)
  • \(B \to B\)\(\max_{i \in B} \{ds_i\}\)

\(x\)原来在\(B\)集合,现在不在\(B\)集合,则删除\(ds_x\)
\(x\)在反图中连接的点都存在第二种情况,删除。

下一次删除前,\(x\)\(A\)集合,则加入\(dt_x\)
\(x\)在图中连接的点都存在第二种情况,加入。

发现需要维护一个支持删除加入,求最值的东西,那么可以使用平衡树权值线段树。(其实multiset应该可以,但是我不会用)

# include<bits/stdc++.h>
using namespace std;
const int N = 500005,M = 1000005;
int n,m;
vector <int> g[N],g2[N];
int ds[N],dt[N];
int du[N],du2[N];
int Tx[N],Txtot = 0;
int q[N];
void topsort(void) //dt
{
    queue <int> q;
    for(int i = 1; i <= n; i++)
    {
        if(du[i] == 0)
        {
            q.push(i);
            Tx[i] = ++Txtot;
        }
    }
    while(!q.empty())
    {
        int x = q.front(); q.pop();
        for(int i = 0; i < (int)g[x].size(); i++)
        {
            int v = g[x][i];
            dt[v] = max(dt[v],dt[x] + 1);
            if(--du[v] == 0) q.push(v),Tx[v] = ++Txtot;
        }
    }
    return;
}
void topsort2(void)
{
    queue <int> q;
    for(int i = 1; i <= n; i++)
    {
        if(du2[i] == 0)
        {
            q.push(i);
        }
    }
    while(!q.empty())
    {
        int x = q.front(); q.pop();
        for(int i = 0; i < g2[x].size(); i++)
        {
            int v = g2[x][i];
            ds[v] = max(ds[v],ds[x] + 1);
            if(--du2[v] == 0) q.push(v);
        }
    }
    return;
}
bool compare(const int &x,const int &y)
{
    return Tx[x] < Tx[y];
}
struct node
{
    int val,l,r;
}T[N << 2];
int tot = 1;
void build(int root,int l,int r)
{
    if(l == r) {T[root].val = 0,T[root].l = T[root].r = 0; return;}
    int mid = (l + r) >> 1;
    T[root].l = ++tot,T[root].r = ++tot;
    build(T[root].l,l,mid),build(T[root].r,mid + 1,r);
    return;
}
void update(int root,int l,int r,int x,int d)
{
    if(l == r && l == x)
    {
        T[root].val += d;
        return;
    }
    int mid = (l + r) >> 1;
    if(l <= x && x <= mid) update(T[root].l,l,mid,x,d);
    else if(x > mid) update(T[root].r,mid + 1,r,x,d);
    T[root].val = T[T[root].l].val + T[T[root].r].val;
    return;
}
int query(int root,int l,int r)
{
    if(l == r) return l;
    int mid = (l + r) >> 1;
    if(T[T[root].r].val > 0) return query(T[root].r,mid + 1,r);
    else return query(T[root].l,l,mid);
}
int main(void)
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i++)
    {
        int a,b; scanf("%d%d",&a,&b);
        g[a].push_back(b),g2[b].push_back(a);
        ++du[b],++du2[a];
    }
    topsort(),topsort2();
    build(1,1,2 * n);
    for(int i = 1; i <= n; i++)
    {
        update(1,1,2 * n,ds[i],1); q[i] = i;
    }
    sort(q + 1, q + n + 1, compare);
    int ans = 1e9 + 7,id = 0;
    for(int i = 1; i <= n; i++) // delete i
    {
        update(1,1,2 * n,ds[q[i]],-1);
        for(int j = 0; j < (int)g2[q[i]].size(); j++)
        {
            int v = g2[q[i]][j];
            update(1,1,2 * n,dt[v] + 1 + ds[q[i]],-1);
        }
        // printf("i = %d,max = %d\n",i,Max);
        int Max = query(1,1,2 * n);
        // printf("i = %d,Max = %d\n",i,Max);
        if(Max < ans) 
        {
            ans = Max,id = q[i];
        }
        // S.insert(dt[i]);
        update(1,1,2 * n,dt[q[i]],1);
        for(int j = 0; j < (int)g[q[i]].size(); j++)
        {
            int v = g[q[i]][j];
            update(1,1,2 * n,dt[q[i]] + 1 + ds[v],1);
        }
    }
    printf("%d %d\n",id,ans);
    return 0;
}

P3573 [POI2014]RAJ-Rally

上一篇:287. 寻找重复数


下一篇:Oracle函数使用1