洛咕P4180 嚴格次小生成樹
阿新 • • 發佈:2018-12-12
鴿了很久的一道題(?)貌似是去年NOIP前聽的emm...
首先我們分析一下最小生成樹的性質
我們kruskal建樹的時候呢是從小到大貪心加的邊,這個的證明用到擬陣。(我太菜了不會)
首先我們不存在連線非樹邊比當前優的情況。
emm我們好像也就用這一條性質就夠了。
步入正題
根據我們剛剛說的性質,我們可以列舉每一條邊,使它和原來的樹邊形成一個環,然後我們需要求環上最大值,讓我們的非樹邊替換掉這個邊形成新的生成樹。很顯然這條邊不會小於最大邊,因為如果小於最大邊的話,我們用這條邊替換掉最大邊會形成更小的生成樹。如果這條邊剛好等於最大邊的話,那麼我們求出來的不是嚴格次小生成樹,而是非嚴格,因為兩棵樹的邊權和相等。那麼如果我們的非樹邊和最大值相等我們就不考慮了嘛(?)很顯然不可以,因為我們可能有非樹邊-次大邊更優的情況。所以我們維護鏈上最大值和次大值就可以啦。
Step1:建立最小生成樹(這個很顯然嘛,既然要求嚴格次小生成樹,你肯定得先有棵樹嘛)
Step2:處理生成樹資訊。
Step3:列舉每條非樹邊更新答案。
我來解釋一下處理生成樹資訊都有啥。我們根據剛剛說的,我們需要維護鏈上最大值和次大值以及求LCA。這一步可以使用許多做法。我用的是倍增,然後樹鏈剖分和LCT都是可以維護的。我才不想寫LCT呢(傲嬌臉)
容易錯的地方的話我們一定要注意維護的次大值要嚴格小於最大值。
然後就到更新答案了。我們列舉的非樹邊有兩種可能,橫叉邊or返祖邊。分別討論一下環的形態求鏈上比它嚴格小的最大值就可以了。
竟然1Abook思議
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define maxn 101000 #define maxm 301000 #define lgn 18 #define ll long long #define inf 2002122520021225ll using namespace std; int f[maxn][lgn+2],mx[maxn][lgn+2],sc[maxn][lgn+2]; struct edge{int u,v,val;}e[maxm]; struct Edge{int to,lt,val;}E[maxn<<1]; int in[maxn],cnt,dep[maxn];bool used[maxm]; bool cmp(edge a,edge b){return a.val<b.val;} int fa[maxn],n,m;ll sum,ans;bool vis[maxn]; void add(int x,int y,int v) { E[++cnt].lt=in[x];E[cnt].to=y;E[cnt].val=v;in[x]=cnt; E[++cnt].lt=in[y];E[cnt].to=x;E[cnt].val=v;in[y]=cnt; } int find(int x) { int i=x,j; while(fa[x]!=x) x=fa[x]; while(i!=x) j=fa[i],fa[i]=x,i=j; return x; } void dfs(int x) { vis[x]=1; for(int i=1;i<=lgn;i++) { f[x][i]=f[f[x][i-1]][i-1]; mx[x][i]=max(mx[x][i-1],mx[f[x][i-1]][i-1]); sc[x][i]=max(sc[x][i-1],sc[f[x][i-1]][i-1]); if(mx[x][i-1]>mx[f[x][i-1]][i-1]) sc[x][i]=max(sc[x][i],mx[f[x][i-1]][i-1]); else if(mx[x][i-1]<mx[f[x][i-1]][i-1]) sc[x][i]=max(sc[x][i],mx[x][i-1]); } for(int i=in[x];i;i=E[i].lt) { int v=E[i].to; if(vis[v]) continue; f[v][0]=x;mx[v][0]=E[i].val;dep[v]=dep[x]+1;dfs(v); } } int jump(int x,int len) { for(int i=lgn;~i;i--) if(len&(1<<i)) x=f[x][i]; return x; } int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); x=jump(x,dep[x]-dep[y]); if(x==y) return x; for(int i=lgn;~i;i--) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int getlink(int x,int len,int mm) { int qwq=0; for(int i=lgn;~i;i--) if(len&(1<<i)) { if(mx[x][i]==mm) qwq=max(qwq,sc[x][i]); else qwq=max(qwq,mx[x][i]); x=f[x][i]; } return qwq; } void kruskal() { int x,i,y,lca; sort(e+1,e+m+1,cmp); for(i=1;i<=n;i++) fa[i]=i; for(i=1;i<=m;i++) { x=find(e[i].u),y=find(e[i].v); if(x!=y) { fa[x]=y;ans+=(ll)e[i].val; add(e[i].u,e[i].v,e[i].val); used[i]=1; } } dep[1]=1;dfs(1);sum=inf; for(i=1;i<=m;i++) { if(!used[i]) { x=e[i].u;y=e[i].v; lca=LCA(x,y); if(dep[x]<dep[y]) swap(x,y); if(y==lca) sum=min(sum,ans-getlink(x,dep[x]-dep[lca],e[i].val)+e[i].val); else sum=min(sum,ans-max(getlink(x,dep[x]-dep[lca],e[i].val),getlink(y,dep[y]-dep[lca],e[i].val))+e[i].val); } } printf("%lld\n",sum); } int main() { int i; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].val); kruskal(); return 0; }