NOIP 模拟 $94\; \rm 欢乐豆$
题解 \(by\;zj\varphi\)
把 \(x\rightarrow y\) 边权值的修改转换成 \(x,y\) 之间连了一条无向边。
先求出来在联通块中点对之间到达的最短距离,而联通块内的点到联通块外的点一定是先走到联通块边界再走到外界。
这就是求 \(\min dis_v+a_v\)
使用 \(dijkstra\) 算法,发现更新最短路的时候有大部分边权值都是一样的,这样直接在线段树上模拟即可。
具体看代码。
Code
#include<bits/stdc++.h>
#define ri signed
#define pd(i) ++i
#define bq(i) --i
#define func(x) std::function<x>
namespace IO{
char buf[1<<21],*p1,*p2;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?(-1):*p1++
#define dg1(x) std::cerr << #x"=" << x << ' '
#define dg2(x) std::cerr << #x"=" << x << std::endl
#define Dg(x) assert(x)
struct nanfeng_stream{
template<typename T>inline nanfeng_stream &operator>>(T &x) {
bool f=false;x=0;char ch=gc();
while(!isdigit(ch)) f|=ch=='-',ch=gc();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=gc();
return x=f?-x:x,*this;
}
}cin;
}
using IO::cin;
namespace nanfeng{
#define int long long
#define pb emplace_back
#define FI FILE *IN
#define FO FILE *OUT
template<typename T>inline T cmax(T x,T y) {return x>y?x:y;}
template<typename T>inline T cmin(T x,T y) {return x>y?y:x;}
using ll=long long;
static const int N=1e5+7,M=3e3+7;
static const ll INF=1e18;
struct TO{int v,w;};
int a[N],fa[N],siz[N],id[N],nwi[N],cnt,n,m;
ll dis[N],ans;
std::vector<TO> G[N];
std::vector<int> vc[N];
std::multiset<int> st;
func(int(int)) find=[](int x) {return x==fa[x]?x:fa[x]=find(fa[x]);};
struct Seg{
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
struct segmenttree{int x;ll ds,lz;bool tg;}T[M<<2];
func(void(int)) up=[&](int x) {
T[x].ds=cmin(T[ls(x)].ds,T[rs(x)].ds);
if (T[x].ds==T[ls(x)].ds&&!T[ls(x)].tg) T[x].x=T[ls(x)].x;
else T[x].x=T[rs(x)].x;
T[x].tg=T[ls(x)].tg&T[rs(x)].tg;
};
func(void(int)) down=[&](int x) {
if (T[x].lz==INF) return;
if (!T[ls(x)].tg) {
T[ls(x)].ds=cmin(T[ls(x)].ds,T[x].lz);
T[ls(x)].lz=cmin(T[ls(x)].lz,T[x].lz);
}
if (!T[rs(x)].tg) {
T[rs(x)].ds=cmin(T[rs(x)].ds,T[x].lz);
T[rs(x)].lz=cmin(T[rs(x)].lz,T[x].lz);
}
T[x].lz=INF;
};
func(void(int,int,int)) build=[&](int x,int l,int r) {
T[x].lz=INF,T[x].tg=false;
if (l==r) return T[x].ds=INF,T[x].x=l,void();
int mid=(l+r)>>1;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
up(x);
};
func(void(int,ll,int,int,int,int)) update=[&](int x,ll k,int l,int r,int lt,int rt) {
if (T[x].tg) return;
if (l<=lt&&rt<=r)
return T[x].ds=cmin(T[x].ds,k),T[x].lz=cmin(T[x].lz,k),void();
int mid=(lt+rt)>>1;
down(x);
if (l<=mid) update(ls(x),k,l,r,lt,mid);
if (r>mid) update(rs(x),k,l,r,mid+1,rt);
up(x);
};
func(void(int,int,int,int)) cover=[&](int x,int p,int l,int r) {
if (l==r) return T[x].ds=INF,T[x].tg=true,void();
int mid=(l+r)>>1;
down(x);
if (p<=mid) cover(ls(x),p,l,mid);
else cover(rs(x),p,mid+1,r);
up(x);
};
}T;
inline int main() {
FI=freopen("happybean.in","r",stdin);
FO=freopen("happybean.out","w",stdout);
cin >> n >> m;
for (ri i(1);i<=n;pd(i)) cin >> a[fa[i]=i],st.insert(a[i]),siz[i]=1;
for (ri i(1),u,v,w;i<=m;pd(i)) {
cin >> u >> v >> w;
G[u].pb((TO){v,w});
u=find(u),v=find(v);
if (u!=v) fa[v]=u,siz[u]+=siz[v];
}
for (ri i(1);i<=n;pd(i))
std::sort(G[i].begin(),G[i].end(),[](const TO &g1,const TO &g2) {return g1.v<g2.v;});
for (ri i(1);i<=n;pd(i)) if (find(i)==i&&siz[i]>1) id[i]=++cnt;
for (ri i(1);i<=n;pd(i))
if (siz[find(i)]>1) vc[id[find(i)]].pb(i);
else ans+=1ll*a[i]*(n-1);
for (ri i(1);i<=cnt;pd(i)) {
const int s=vc[i].size();
for (ri j(0);j<s;pd(j)) {
const int nw=vc[i][j];
nwi[nw]=j+1,st.erase(st.find(a[nw]));
}
ll tmp=st.size()?*st.begin():INF;
for (auto u:vc[i]) {
st.insert(a[u]);
T.build(1,1,s);
T.update(1,0,nwi[u],nwi[u],1,s);
while(!T.T[1].tg&&T.T[1].ds!=INF) {
int x=T.T[1].x;
dis[x]=T.T[1].ds;
ll nd=dis[x];
T.cover(1,x,1,s);
x=vc[i][x-1];
int lst=1;
for (auto v:G[x]) {
const int nv=nwi[v.v];
if (lst<=nv-1) T.update(1,nd+a[x],lst,nv-1,1,s);
T.update(1,nd+v.w,nv,nv,1,s);
lst=nv+1;
}
if (lst<=s) T.update(1,nd+a[x],lst,s,1,s);
}
ll pa=ans;
for (ri j(1);j<=s;pd(j)) ans+=cmin(dis[j],a[u]+tmp);
ll tpm=INF;
for (ri j(1);j<=s;pd(j)) tpm=cmin(tpm,(dis[j]+a[vc[i][j-1]])*(n-s));
ans+=tpm;
}
}
printf("%lld\n",ans);
return 0;
}
#undef int
}
int main() {return nanfeng::main();}