bzoj2001: [Hnoi2010]City 城市建設 wikioi2332
阿新 • • 發佈:2019-02-02
/************************************************************** Problem: 2001 User: xujiahe Language: C++ Result: Accepted Time:4084 ms Memory:24656 kb ****************************************************************/ #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cmath> using namespace std; #define maxn 51000 struct quest { int x,y; }q[60000]; struct node { int pos,x,y,w; }e[25][maxn],d[maxn],t[maxn];//e[][]用來記錄分層圖,d[]記錄當前圖,t[]可以當做中間變數。 int n,m,Q; int father[maxn],val[maxn],c[maxn],size[maxn],sum[maxn]; long long ans[maxn]; inline bool cmp(node aa,node bb) { return aa.w<bb.w; } inline int getfather(int x){return x==father[x] ? x:father[x]=getfather(father[x]);} inline void clear(int tot)//初始化,把圖還原成點 { for(int i=1;i<=tot;i++) { father[d[i].x]=d[i].x; father[d[i].y]=d[i].y; size[d[i].x]=1; size[d[i].y]=1; } } inline void merge(int x,int y) { if (size[x]<=size[y]) size[y]+=size[x],father[x]=y;//size[]用來加速(再次查詢時只查個數少的一組) else size[x] += size[y],father[y] = x; } void contraction(int &tot,long long &cnt)//縮必須邊 { int tmp=0;//記錄邊的個數 clear(tot); sort(d+1,d+1+tot,cmp); //把要改動的邊加進MST,如果這時還要邊i說明i是必須邊,無論如何都要存到下一個圖裡 for(int i=1;i<=tot;i++) { int r1=getfather(d[i].x); int r2=getfather(d[i].y); if(r1!=r2) { merge(r1,r2); tmp++; t[tmp]=d[i]; } } //初始化,再次做MST for(int i=1;i<=tmp;i++) { father[t[i].x]=t[i].x; father[t[i].y]=t[i].y; size[t[i].x]=1; size[t[i].y]=1; } for(int i=1;i<=tmp;i++) { int r1=getfather(t[i].x); int r2=getfather(t[i].y); if(r1!=r2&&t[i].w!=-0x3f3f3f3f)//再次做MST時把邊值不為最小的邊(必須邊)找出來,加上邊的值 { merge(r1,r2); cnt+=t[i].w; } } tmp=0; //注意此時必須邊的權值已經記錄,點也都放入了一個集合,所以可以把這些端點都看成一個點。 for ( int i=1 ; i<=tot ; i++) { int r1 = getfather(d[i].x); int r2 = getfather(d[i].y); if( r1 != r2) { t[++tmp]=d[i]; c[d[i].pos] = tmp; t[tmp].x = father[d[i].x]; t[tmp].y = father[d[i].y]; } } tot=tmp; for ( int i=1 ; i<=tot ; i++ ) { d[i]=t[i]; } } inline void reduction(int &tot)//刪除無用邊 { int tmp=0; clear(tot); sort(d+1,d+1+tot,cmp); //如果把要改變的邊都設為最大值,也用不到邊i,那麼邊i為廢邊 for(int i=1;i<=tot;i++) { int r1=getfather(d[i].x); int r2=getfather(d[i].y); if(r1!=r2) { merge(r1,r2); tmp++;//記錄有用的邊 t[tmp]=d[i]; c[d[i].pos]=tmp; } else if(d[i].w==0x3f3f3f3f)//加入要改變的邊 { tmp++; t[tmp]=d[i]; c[d[i].pos]=tmp; } } for(int i=1;i<=tmp;i++) d[i]=t[i]; tot=tmp; } inline void solve(int l,int r,int now,long long cnt)//now為圖的序號 { int tot=sum[now]; if(l==r)//遞迴到底了 { val[q[l].x]=q[l].y; } for(int i=1;i<=tot;i++)//存圖 { e[now][i].w=val[e[now][i].pos]; } for(int i=1;i<=tot;i++) { d[i]=e[now][i]; c[d[i].pos]=i;//記錄d[]中邊的序號。 } if(l==r)//只改變1條邊所以直接進行排序。 { clear(tot); ans[l]=cnt; sort(d+1,d+1+tot,cmp); for(int i=1;i<=tot;i++) { int r1=getfather(d[i].x); int r2=getfather(d[i].y); if(r1!=r2) { merge(r1,r2); ans[l]+=d[i].w; } } return; } for(int i=l;i<=r;i++) d[c[q[i].x]].w=-0x3f3f3f3f;//改變了邊得值,val[]和c[]就體現了他們的價值。 contraction(tot,cnt);//分析見函式 for(int i=l;i<=r;i++) d[c[q[i].x]].w=0x3f3f3f3f; reduction(tot);//分析見函式 //經過上面兩個操作,縮邊和刪廢邊 圖應該變得越來越小。 for(int i=1;i<=tot;i++) e[now+1][i]=d[i];//存下一個序號的圖 int mid=(l+r)>>1;//對訊問進行分治 sum[now+1]=tot; solve(l,mid,now+1,cnt); solve(mid+1,r,now+1,cnt); } int main() { int x,y,z; scanf("%d%d%d",&n,&m,&Q); for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&val[i]); e[0][i].x=x;//初始圖的序號為0 e[0][i].y=y; e[0][i].w=val[i]; e[0][i].pos=i;//記錄編號。 } for(int i=1;i<=Q;i++) { scanf("%d%d",&q[i].x,&q[i].y); } sum[0]=m;//sum記錄每個圖有幾條有用邊 solve(1,Q,0,0);//對訊問進行分治 for(int i=1;i<=Q;i++) { printf("%lld\n",ans[i]); } return 0; }