n(≤10w)个点m(≤10w)条边的无向联通图,回答每一条边是属于任何一个MST(minimum spanning tree),还是不属于任何一个MST,还是属于至少一个MST。
关于正解。
桥+dsu
By Kyle Young :
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int tot_edge,head[N],dfs_clock,pre[N],par[N],ans[N];
// ans[i]=0:any 1:at least one 2:none
void rd(int &res){
res=0;
char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
struct EDGE{
int u,v,cost,id;
inline void Rd(){
rd(u);rd(v);rd(cost);
}
inline bool operator <(const EDGE &tmp)const{
return cost<tmp.cost;
}
}es[N];
struct Edge{
int to,nxt,id;
}edge[N<<1];
void add_edge(int u,int v,int id){
edge[tot_edge]=(Edge){v,head[u],id};
head[u]=tot_edge++;
}
int get_root(int x){
return par[x]==x?x:par[x]=get_root(par[x]);
}
inline void Min(int &a,int b){
if(b<a)a=b;
}
int dfs(int u,int id){
int lowu=pre[u]=++dfs_clock;
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].to;
if(!pre[v]){
int lowv=dfs(v,i);
Min(lowu,lowv);
if(lowv>pre[u])ans[edge[i].id]=0;
}
else if(pre[v]<pre[u]&&i!=(id^1)){
Min(lowu,pre[v]);
}
}
return lowu;
}
void unite(int u,int v){
u=get_root(u);
v=get_root(v);
if(u==v)return;
par[u]=v;
}
int main(){
int n,m;
rd(n);rd(m);
for(int i=1;i<=n;++i)
par[i]=i;
for(int i=0;i<m;++i){
es[i].Rd();
es[i].id=i;
}
sort(es,es+m);
for(int i=0;i<m;){
int j=i;
while(j+1<m&&es[j+1].cost==es[j].cost)++j;
tot_edge=dfs_clock=0;
for(int k=i;k<=j;++k){
es[k].u=get_root(es[k].u);
es[k].v=get_root(es[k].v);
int u=es[k].u,v=es[k].v;
head[u]=head[v]=-1;
pre[u]=pre[v]=0;
}
for(int k=i;k<=j;++k){
int u=es[k].u,v=es[k].v,id=es[k].id;
if(u==v){
ans[id]=2;
continue;
}
ans[id]=1;
add_edge(u,v,id);
add_edge(v,u,id);
}
for(int k=i;k<=j;++k){
if(!pre[es[k].u])dfs(es[k].u,-1);
}
for(;i<=j;++i)
unite(es[i].u,es[i].v);
}
for(int i=0;i<m;++i){
if(ans[i]==2)puts("none");
else puts(ans[i]?"at least one":"any");
}
return 0;
}
关于树链剖分水过。
By ShinFeb(me)
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#define pi acos(-1)
#define inf (1<<30)
#define INF (1<<62)
#define CLR(x,f) memset(x,f,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define prt(x) cout<<#x<<": "<<x<<endl
#define huh(x) printf("--------DEBUG(%d)--------\n",x)
#define travel(x) for(Edge *e=h[x];e;e=e->n)
//#define TL
using namespace std;
const int M=100005;
int ans[M],n,m;
struct Edge{
Edge *n;
int to,c,id;
Edge(){}
}*h[M];
inline void addEdge(int u,int v,int w,int id){
Edge *x=new Edge();
x->to=v;x->c=w;x->n=h[u];x->id=id;
h[u]=x;
}
struct EDGE{
int u,v,w,id;
bool operator<(const EDGE&a)const{
return w<a.w;
}
}edge[M];
int val[M],link[M],par[M];
int sz[M],dep[M];
int frm[M];
int segID[M],dfsID[M];
//dfs序->sgm sgm->dfs序
int dfs_clock;
void dfs(int x,int f){
sz[x]=1;par[x]=f;dep[x]=dep[f]+1;
travel(x)if(e->to!=f){
val[e->to]=e->c;
frm[e->to]=e->id;
dfs(e->to,x);
sz[x]+=sz[e->to];
}
}
void assign(int x,int l){
link[x]=l;
segID[x]=++dfs_clock;
dfsID[dfs_clock]=x;
int k=0;
travel(x)if(sz[e->to]<sz[x]&&sz[e->to]>sz[k])k=e->to;
if(!k)return;
assign(k,l);
travel(x)if(sz[e->to]<sz[x]&&e->to!=k)assign(e->to,e->to);
}
struct node{
int mx,id;
node(){}
node(int mx_,int id_):mx(mx_),id(id_){}
bool operator<(const node&a)const{
if(mx!=a.mx)return mx<a.mx;
return id<a.id;
}
};
void Max(node &x,node y){
if(x<y)x=y;
}
struct Segment{
node v[M<<2];
void up(int x){
v[x]=max(v[x<<1],v[x<<1|1]);
}
void build(int l,int r,int x){
if(l==r){
v[x].mx=val[dfsID[l]];
v[x].id=dfsID[l];
return;
}
int mid=l+r>>1;
build(l,mid,x<<1);
build(mid+1,r,x<<1|1);
up(x);
}
void clear(int l,int r,int x,int t){
if(l==r){
v[x].id=0;
return;
}
int mid=l+r>>1;
if(t<=mid)clear(l,mid,x<<1,t);
else clear(mid+1,r,x<<1|1,t);
up(x);
}
node query(int l,int r,int x,int L,int R){
if(L<=l&&r<=R)return v[x];
int mid=l+r>>1;
node res=node(0,0);
if(L<=mid)Max(res,query(l,mid,x<<1,L,R));
if(R>mid)Max(res,query(mid+1,r,x<<1|1,L,R));
return res;
}
}sgm;
int fa[M];
int get(int x){
return fa[x]==x?x:fa[x]=get(fa[x]);
}
void buildMST(){
sort(edge+1,edge+m+1);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int u=edge[i].u,v=edge[i].v;
if(get(u)==get(v))continue;
fa[get(u)]=get(v);
ans[edge[i].id]=true;
addEdge(u,v,edge[i].w,edge[i].id);
addEdge(v,u,edge[i].w,edge[i].id);
}
}
void HLD(){
dfs(1,0);
assign(1,1);
sgm.build(1,n,1);
}
bool chk(int u,int v,int w){//check u-v的路径上是否有边权==w的边
bool flag=false;
while(link[u]!=link[v]){//取尾不取首
if(dep[link[u]]<dep[link[v]])swap(u,v);
node res=sgm.query(1,n,1,segID[link[u]],segID[u]);
if(res.mx==w){
while(res.id){
ans[frm[res.id]]=2;
sgm.clear(1,n,1,segID[res.id]);
res=sgm.query(1,n,1,segID[link[u]],segID[u]);
}
flag=true;
}
u=par[link[u]];
}
if(u==v)return flag;
if(dep[u]<dep[v])swap(u,v);
node res=sgm.query(1,n,1,segID[v]+1,segID[u]);
if(res.mx==w){
while(res.id){
ans[frm[res.id]]=2;
sgm.clear(1,n,1,segID[res.id]);
res=sgm.query(1,n,1,segID[v]+1,segID[u]);
}
flag=true;
}
return flag;
}
void solve(){
for(int i=1;i<=m;i++)
if(!ans[edge[i].id])//非树边
if(chk(edge[i].u,edge[i].v,edge[i].w))ans[edge[i].id]=2;
}
int main(){
#ifdef TL
#endif
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
edge[i].id=i;
}
buildMST();
HLD();
solve();
for(int i=1;i<=m;i++){
if(ans[i]==0)puts("none");
else if(ans[i]==1)puts("any");
else puts("at least one");
}
return 0;
}
还有一份奥妙重重的代码。
By ShinFeb again
dsu+dfs序
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int M=50005;
struct Edge{
int a,b,c,id;
bool operator<(const Edge&t)const{
return c<t.c;
}
}edge[M<<2];
struct EDGE{
int to,nxt;
}e[M<<1];
int n,m;
int mark[M<<2],size[M];
int fa[M],cov[M],par[M];
int last[M],allc,dep[M];
int pos[M<<2];
void ins(int a,int b){
e[++allc]=(EDGE){b,last[a]};last[a]=allc;
e[++allc]=(EDGE){a,last[b]};last[b]=allc;
}
int get(int a){
return fa[a]==a?a:fa[a]=get(fa[a]);
}
void dfs(int x,int f){
fa[x]=x;par[x]=f;dep[x]=dep[x]=dep[f]+1;
for(int i=last[x];i;i=e[i].nxt)
if(e[i].to!=f)dfs(e[i].to,x);
}
void col(int a,int b,int c){
while(a=get(a),b=get(b),a!=b){
if(dep[a]<dep[b])swap(a,b);
cov[a]=c;fa[a]=par[a];
}
}
int judge(){
for(int i=1;i<=n;i++)fa[i]=i,size[i]=1;
int cost=0;
for(int i=1;i<=m;i++){
int a=edge[i].a,b=edge[i].b;
a=get(a),b=get(b);
if(a==b)continue;
mark[i]=1;
ins(edge[i].a,edge[i].b);
fa[a]=b;size[b]+=size[a];
cost+=edge[i].c;
}
return size[get(1)]==n?cost:0;
}
int main(){
scanf("%d%d",&n,&m);
for(int a,b,c,i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
edge[i]=(Edge){a,b,c,i};
}
sort(edge+1,edge+m+1);
int ans=judge();
if(!ans){
for(int i=0;i<m;i++)puts("-1");
return 0;
}
for(int i=1;i<=m;i++)
pos[edge[i].id]=i;
dfs(1,0);
for(int i=1;i<=m;i++)
if(!mark[i])
col(edge[i].a,edge[i].b,i);
for(int i=1;i<=m;i++){
if(!mark[pos[i]]){
printf("%d\n",ans);
continue;
}
int a=edge[pos[i]].a,b=edge[pos[i]].b;
if(dep[a]<dep[b])a=b;
if(!cov[a])puts("-1");
else printf("%d\n",ans-edge[pos[i]].c+edge[cov[a]].c);
}
return 0;
}