link
sol
其实这道题的建边还很很有思维含量的
通过分析我们发现,一条边可以走无非两种情况
-
花费 \(w_i\) 将这条边改成其他颜色
-
花费 \(sum_{i,c}-w_i\) 将其他同种颜色的边改掉
但是这样有一种特殊情况无法处理,从x->u->v 是同一种颜色的,但只需要改一条就好了
所以考虑每个点上都放一些虚点 \(p_{i,c}\)
对于每个点来说,如果 \(x\) 有一个 \(y\) 与它相连,那么建两条边\((x,p_{y,c},0,0)(p_{y,c},x,sum_{x,c}-w_{x->y})\) 表示格一个建立起联系
然后刷一次最短路就好了
code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int maxn=3e5+5,maxe=16e5+5,maxm=2e5+5;
int N,M;
int lnk[maxn],son[maxe],nxt[maxe],cnt,w[maxe],col[maxe];
int tot,sum[maxm],num[maxm],rt[maxm];
int vis[maxn],Q[maxn];
LL dis[maxn];
inline void add_e(int x,int y,int c_,int w_){
son[++cnt]=y;nxt[cnt]=lnk[x];col[cnt]=c_;w[cnt]=w_;lnk[x]=cnt;
}
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void spfa(){
memset(dis,INF,sizeof dis);
dis[1]=0;int til=1,hed=0;Q[til]=1;
while(hed!=til){
hed=(hed+1)%maxn;vis[Q[hed]]=0;
for(int j=lnk[Q[hed]];j;j=nxt[j])
if(dis[Q[hed]]+w[j]<dis[son[j]]){
dis[son[j]]=dis[Q[hed]]+w[j];
if(!vis[son[j]]){vis[Q[til=(til+1)%maxn]=son[j]]=1;}
}
}
}
int main(){
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
N=read();M=read();
for(int i=1;i<=M;i++){
int x=read(),y=read(),c_=read(),w_=read();
add_e(x,y,c_,w_);add_e(y,x,c_,w_);
}
tot=N;
for(int i=1;i<=N;i++){
for(int j=lnk[i];j;j=nxt[j])if(col[j]){
sum[col[j]]+=w[j];num[col[j]]++;
}
for(int j=lnk[i];j;j=nxt[j])if(col[j]){
if(num[col[j]]==1)w[j]=0;
else{
if(!rt[col[j]]) rt[col[j]]=++tot,add_e(i,tot,0,0);
add_e(rt[col[j]],son[j],0,sum[col[j]]-w[j]);
add_e(son[j],rt[col[j]],0,0);
}
}
for(int j=lnk[i];j;j=nxt[j]) rt[col[j]]=sum[col[j]]=num[col[j]]=0;
}
spfa();
printf("%lld\n",(dis[N]^INF)?dis[N]:-1);
return 0;
}