1. 程式人生 > >Nowcoder 練習賽 23 D Where are you 解題報告

Nowcoder 練習賽 23 D Where are you 解題報告

Where are you

連結:

https://ac.nowcoder.com/acm/contest/272/D

來源:牛客網

題目描述

\(p\)和他的朋友約定好去遊樂場遊玩,但是他們到了遊樂場後卻互相找不到對方了。
遊樂場可以看做是一張\(n\)個點,\(m\)條道路的圖,每條道路有邊權\(w_i\),表示第一次經過該道路時的花費(第二次及以後經過時花費為\(0\))。
現在,小\(p\)要去找他的朋友,但他的朋友行蹤很詭異,小\(p\)總是要遍歷完這\(n\)個點才能找到他,同時小\(p\)希望總花費最小。
找到朋友的方案可能不唯一,小\(p\)想知道在這所有的方案中,有多少條邊在每個方案中都會被經過。

輸入描述:

第一行兩個整數\(n\), \(m\)\(p\),分別表示點數,邊數,小\(p\)的初始位置。
接下來\(m\)行,每行兩個整數\(u\), \(v\), \(w\)表示從\(u\)\(v有\)一條無向邊,邊權為\(w\)

輸出描述:

輸出一個整數k,表示必須經過的邊的數量。

說明

\(2\le n < m \le 2\times 10^5,1 \le w \le 10^6\)

保證圖聯通,無自環無重邊


比賽的時候wa了一晚上,-8了...後來發現錯誤是沒有判\(set\)空然後\(*s.begin()\)\(set\)為空的時候居然是\(0\)...

正解不是很懂,想了一個\(n\log^2n\)的辣雞做法。

思路:

先求出\(MST\),然後列舉不在\(MST\)上的邊。如果這個邊的兩點在樹上的路徑中的邊的權值小於等於這條邊,那麼樹上的那條路徑就是可以被取代的。

實現就是對兩個點及它們的\(LCA\)打樹上差分,然後\(set\)啟發式合併一下就好了。


Code:

#include <cstdio>
#include <algorithm>
#include <set>
#include <vector>
const int N=2e5+10;
struct node
{
    int u,v,w;
    bool friend operator <(node n1,node n2){return n1.w<n2.w;}
}E[N];
int n,m,f[N],used[N],ans;
int Find(int x){return f[x]=f[x]==x?x:Find(f[x]);}
void Merge(int x,int y){f[Find(x)]=Find(y);}
int head[N],to[N<<1],Next[N<<1],edge[N<<1],cnt;
void add(int u,int v,int w)
{
    to[++cnt]=v,Next[cnt]=head[u],edge[cnt]=w,head[u]=cnt;
}
void krus()
{
    for(int i=1;i<=n;i++) f[i]=i;
    std::sort(E+1,E+1+m);
    for(int i=1;i<=m;i++)
    {
        int u=E[i].u,v=E[i].v,w=E[i].w;
        if(Find(u)!=Find(v))
        {
            Merge(u,v);
            used[i]=1;
            add(u,v,w),add(v,u,w);
        }
    }
}
int F[N][20],dep[N];
void dfs1(int now,int fa)
{
    for(int i=1;F[now][i-1];i++) F[now][i]=F[F[now][i-1]][i-1];
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(v!=fa)
        {
            F[v][0]=now;
            dep[v]=dep[now]+1;
            dfs1(v,now);
        }
    }
}
std::multiset <int > s[N];
std::vector <int > poi[N];
void dfs2(int now,int fa)
{
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(v==fa) continue;
        dfs2(v,now);
        if(s[v].empty()||*(s[v].begin())>edge[i]) ++ans;
        if(s[now].size()<s[v].size()) std::swap(s[now],s[v]);
        for(std::multiset<int>::iterator it=s[v].begin();it!=s[v].end();it++)
            s[now].insert(*it);
        s[v].clear();
    }
    for(int i=0;i<poi[now].size();i++)
    {
        if(poi[now][i]<0)
            s[now].erase(s[now].find(-poi[now][i]));
        else
            s[now].insert(poi[now][i]);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) return LCA(y,x);
    for(int i=18;~i;i--)
        if(dep[F[x][i]]>=dep[y])
            x=F[x][i];
    if(x==y) return x;
    for(int i=18;~i;i--)
        if(F[x][i]!=F[y][i])
            x=F[x][i],y=F[y][i];
    return F[x][0];
}
bool cmp(int n1,int n2){return n1>n2;}
int main()
{
    int p;scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
    krus();
    dep[1]=1;dfs1(1,1);
    for(int i=1;i<=m;i++)
    {
        int u=E[i].u,v=E[i].v,w=E[i].w;
        if(used[i]) continue;
        int lca=LCA(u,v);
        poi[u].push_back(w);
        poi[v].push_back(w);
        poi[lca].push_back(-w);
        poi[lca].push_back(-w);
    }
    dfs2(1,1);
    printf("%d\n",ans);
    return 0;
}

2018.12.1