[JZOJ5465]道路重建--邊雙縮點+樹的直徑
阿新 • • 發佈:2018-10-21
clu 過程 縮點 main har ret cstring 樹邊 似的
題目鏈接
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]道路重建--邊雙縮點+樹的直徑