PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。
PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。
文件第一行包含三个整数N,M,Q,分别表示城市的数目,可以修建的道路个数,及收到的消息个数。 接下来M行,第i+1行有三个用空格隔开的整数Xi,Yi,Zi(1≤Xi,Yi≤n, 0≤Zi≤50000000),表示在城市Xi与城市Yi之间修建道路的代价为Zi。接下来Q行,每行包含两个数k,d,表示输入的第k个道路的修建代价修改为d(即将Zk修改为d)。
输出包含Q行,第i行输出得知前i条消息后使城市连通的最小花费总和。
【数据规模】 对于20%的数据, n≤1000,m≤6000,Q≤6000。 有20%的数据,n≤1000,m≤50000,Q≤8000,修改后的代价不会比之前的代价低。 对于100%的数据, n≤20000,m≤50000,Q≤50000。
CDQ分治+最小生成树,思路很棒
有两个关键操作:
Reduction(删除无用边):将待修改的边权值改为INF,做一遍MST,将所有权值不等于INF且不在MST中的边删掉。因为MST将来只可能变得更小,这些边一定不会有用。
Construction(缩必须边):将待修改的边权值改为-INF,做一遍MST,将所有权值不等于-INF且在MST中的边称为必须边,对于必须边缩点。因为不管待修改的边权值改为多少,这些边都是必须选的。
每次分治操作,先进行删边和缩点两个操作,然后分治下面一层,分治到最底层的时候修改边权并且计算答案。
然后就是一些细节了,这道题写起来挺麻烦的。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 20010 #define maxm 50010 #define inf 1000000000 using namespace std; int n,m,Q,sum[25],f[maxn],size[maxn],c[maxm],a[maxm]; ll ans[maxn]; struct edge{int x,y,w,pos;}e[25][maxm],d[maxm],t[maxm]; struct data{int x,y;}q[maxm]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline bool cmp(edge a,edge b){return a.w<b.w;} inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);} inline void merge(int x,int y) { if (size[x]<=size[y]) size[y]+=size[x],f[x]=y; else size[x]+=size[y],f[y]=x; } inline void clear(int tot) { F(i,1,tot) { f[d[i].x]=d[i].x;f[d[i].y]=d[i].y; size[d[i].x]=size[d[i].y]=1; } } void constraction(int &tot,ll &cnt) { int tmp=0; clear(tot); sort(d+1,d+tot+1,cmp); F(i,1,tot) { int fx=find(d[i].x),fy=find(d[i].y); if (fx!=fy) merge(fx,fy),t[++tmp]=d[i]; } F(i,1,tmp) { f[t[i].x]=t[i].x;f[t[i].y]=t[i].y; size[t[i].x]=size[t[i].y]=1; } F(i,1,tmp) { int fx=find(t[i].x),fy=find(t[i].y); if (t[i].w!=-inf&&fx!=fy) merge(fx,fy),cnt+=t[i].w; } tmp=0; F(i,1,tot) if (find(d[i].x)!=find(d[i].y)) { t[++tmp]=d[i];c[d[i].pos]=tmp; t[tmp].x=f[d[i].x];t[tmp].y=f[d[i].y]; } tot=tmp;F(i,1,tot) d[i]=t[i]; } void reduction(int &tot) { int tmp=0; clear(tot); sort(d+1,d+tot+1,cmp); F(i,1,tot) { int fx=find(d[i].x),fy=find(d[i].y); if (fx!=fy) merge(fx,fy),t[++tmp]=d[i],c[d[i].pos]=tmp; else if (d[i].w==inf) t[++tmp]=d[i],c[d[i].pos]=tmp; } tot=tmp;F(i,1,tot) d[i]=t[i]; } void solve(int l,int r,int now,ll cnt) { int tot=sum[now]; if (l==r) a[q[l].x]=q[l].y; F(i,1,tot) e[now][i].w=a[e[now][i].pos]; F(i,1,tot) d[i]=e[now][i],c[d[i].pos]=i; if (l==r) { ans[l]=cnt; clear(tot); sort(d+1,d+tot+1,cmp); F(i,1,tot) { int fx=find(d[i].x),fy=find(d[i].y); if (fx!=fy) merge(fx,fy),ans[l]+=d[i].w; } return; } F(i,l,r) d[c[q[i].x]].w=-inf; constraction(tot,cnt); F(i,l,r) d[c[q[i].x]].w=inf; reduction(tot); F(i,1,tot) e[now+1][i]=d[i]; sum[now+1]=tot; int mid=(l+r)>>1; solve(l,mid,now+1,cnt);solve(mid+1,r,now+1,cnt); } int main() { n=read();m=read();Q=read(); int x,y; F(i,1,m){e[0][i].x=read();e[0][i].y=read();e[0][i].w=a[i]=read();e[0][i].pos=i;} F(i,1,Q) q[i].x=read(),q[i].y=read(); sum[0]=m; solve(1,Q,0,0); F(i,1,Q) printf("%lld\n",ans[i]); return 0; }