「NOIP2021模擬賽 By ZYQ D」城闕 題解
阿新 • • 發佈:2021-11-17
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; }