码迷,mamicode.com
首页 > 其他好文 > 详细

[JZOJ5465]道路重建--边双缩点+树的直径

时间:2018-10-21 12:07:56      阅读:222      评论:0      收藏:0      [点我收藏+]

标签:过程   template   name   数组   struct   air   iostream   noip   char   

题目链接

lueluelue

分析

这鬼题卡了我10发提交,之前做过一道类似的题目:https://rye-catcher.github.io/2018/07/09/luogu%E9%A2%98%E8%A7%A3P2860-USACO%E5%86%97%E6%9D%82%E8%B7%AF%E5%BE%84-%E7%BC%A9%E7%82%B9-%E6%A1%A5/

危险的边就是桥边,Tarjan求出边双后缩点整个图变成树,树边都是危险的边,我们需要加一条边构成一个新的ecc使危险的边最小

于是一开始naiive的以为求出所有叶子节点判一判就好了,于是WA*1

发现这个SB思路一看就是错的,又想到APIO 巡逻很像这道题,发现我们只要将树的直径两端点连起来一定是最优的,因为直径上的边都成为联通分量上的了就不是危险的边

于是求个树的直径就好了

结果发现求树的直径过程中会遍历一个环wtf?!虽然不知道为什么会有个环但加上个vis数组就没事了 WA*2

接着交一发95 最后一点 RE 了,这时候才发现边的范围1e6

改了一下又交了一发结果5分只过了最后一个点wtf?! 又是fread的锅 (NOIP都不敢用了)

然后终于A了这题...

代码

/*
  code by RyeCatcher
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <utility>
#include <queue>
#include <vector>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <iostream>
#define DEBUG freopen("dat.in","r",stdin);freopen("wa.out","w",stdout);
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define ri register int
#define ll long long
#define ull unsigned long long
#define SIZE 1<<22
using std::min;
using std::max;
using std::priority_queue;
using std::queue;
using std::vector;
using std::pair;
using namespace __gnu_pbds;
inline char gc(){
    static char buf[SIZE],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while((c=gc())>‘9‘||c<‘0‘)ne=c==‘-‘;x=c-48;
    while((c=gc())>=‘0‘&&c<=‘9‘)x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=400005;
const int M=4000005;
const int inf=0x7fffffff;
struct Edge{
    int ne,to;
}edge[M<<1];
int h[maxn],num_edge=1;
inline void add_edge(int f,int to){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=to;
    h[f]=num_edge;
}
struct QAQ{
    int ne,to;
}se[M<<1];
int sh[maxn],num_se=1;
inline void add_se(int f,int to){
    se[++num_se].ne=sh[f];
    se[num_se].to=to;
    sh[f]=num_se;
}
int n,m;
int dfn[maxn],low[maxn],tot=0;
bool bri[M<<1];
int in_ecc[maxn],cnt=0;
void tarjan(int now,int id){
    int v;
    dfn[now]=low[now]=++tot;
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(!dfn[v]){
            tarjan(v,i);
            low[now]=min(low[now],low[v]);
            if(dfn[now]<low[v]){
                bri[i]=bri[i^1]=1;
            }
        }
        else if(id!=(i^1)){
            low[now]=min(low[now],dfn[v]);
        }
    }
    return;
}
bool vis[maxn];
void color(int now,int fa){
    int v;
    in_ecc[now]=cnt;
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(bri[i]||in_ecc[v])continue;
        color(v,now);
    }
}
int rt,tmp;
void dfs1(int now,int fa,int dis){
    int v;
    vis[now]=1;
    if(dis>tmp){
        rt=now,tmp=dis;
    }
    for(ri i=sh[now];i;i=se[i].ne){
        v=se[i].to;
        if(v==fa||vis[v])continue;
        dfs1(v,now,dis+1);
    }
    return ;
}
int main(){
    int x,y;
    FO(rebuild);
    //freopen("rebuild01.in","r",stdin);
    //freopen("rebuild01.ans","w",stdout);
    while(scanf("%d %d",&n,&m)!=EOF&&(n+m)){
        //printf("--%d %d--\n",n,m);
        int S1=sizeof(bool)*(n+3),S2=sizeof(bool)*(m*2+3);
        for(ri i=1;i<=n;i++){
            h[i]=dfn[i]=sh[i]=in_ecc[i]=vis[i]=0;
        }
        memset(bri,0,S2);//清空桥边标记!!! 
        num_edge=num_se=1;
        tot=cnt=0;
        for(ri i=1;i<=m;i++){
            read(x),read(y);
            add_edge(x,y),add_edge(y,x);
        }
        for(ri i=1;i<=n;i++)if(!dfn[i])tarjan(i,0);
        for(ri i=1;i<=n;i++)if(!in_ecc[i]){
            cnt++;
            color(i,0);
        }
        //printf("%d\n",cnt);
        for(ri i=1;i<=n;i++){
            x=in_ecc[i];
            for(ri j=h[i];j;j=edge[j].ne){
                y=in_ecc[edge[j].to];
                if(x!=y){
                    add_se(x,y);
                    add_se(y,x);
                }
            }
        }
        rt=1,tmp=-1;
        dfs1(1,0,0);
        memset(vis,0,S1);
        dfs1(rt,0,0);
        //printf("%d\n",lef);
        printf("%d\n",cnt-1-tmp);
        //puts("wtf");
    }
    return 0;
}

[JZOJ5465]道路重建--边双缩点+树的直径

标签:过程   template   name   数组   struct   air   iostream   noip   char   

原文地址:https://www.cnblogs.com/Rye-Catcher/p/9824384.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!