hdu_4467_Graph(莫隊算法思想)

題目連接:hdu_4467_Graph

題意:給你n個點,m條邊,每條邊有一個權值,有兩個操作,一個是修改單點的顏色,一個是詢問邊的兩個端點都爲指定顏色的權值和

題解:這題如果暴力的話,就是維護3個ans,一個是兩個端點都爲0的,一個是一個爲1一個爲0的,最後還有個兩個端點都爲1的,對於每個詢問,可以做到O(1),但對於修改單點操作,極限狀態下,一個單點的邊最大可爲1e5,這樣果斷T飛,如果我們對每個單點修改做到O(1),那麼詢問的時候會達到1e5*1e5,也果斷T飛,怎麼辦,這時候想想莫隊的思想,將這兩個操作的時間複雜度均分一下,我們設一個點的邊大於sqrt(m)的爲重點,小於的爲輕點,這樣我們對輕點進行暴力維護就sqrt(m),重點就將他周圍的權值用一個二維數組sum[i][j]維護,表示i這個重點它周圍的j這個顏色的權值和,每次修改重點時,直接在三個ans上對sum加加減減就行了,當修改輕點的時候要注意,輕點周圍的重點的sum要維護一下,設重點(邊大於sqrt(m))的個數爲x,則x*sqrt(m)/2<m,所以x<2*sqrt(m),這樣總的複雜度就降到了nsqrt(m)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define F(i,a,b) for(int i=a;i<=b;i++)
typedef long long LL;
using namespace std;

const int N=(int)1e5+7;
int du[N],sp[N],type[N],g[N][2],v[N<<2],nxt[N<<2],ed,sqr;
LL ans[3],w[N<<2],sum[N][2];
//sp表示爲輕重點(邊數大於sqr的爲重點),type爲當前的類型
struct edge{
	int u,v;LL w;
	bool operator<(const edge &b)const{
		if(u==b.u)return v<b.v;
		return u<b.u;
	}
}e[N];

inline void adg(int t,int x,int y,LL z){v[++ed]=y,w[ed]=z,nxt[ed]=g[x][t],g[x][t]=ed;}

int main(){
	int ic=1,n,m,x;
	while(~scanf("%d%d",&n,&m)){
		F(i,1,n)scanf("%d",&x),type[i]=x;
		F(i,0,m-1){
			scanf("%d%d%lld",&e[i].u,&e[i].v,&e[i].w);
			if(e[i].u>e[i].v)swap(e[i].u,e[i].v);
		}
		//將邊去重並將權值合併
		sort(e,e+m);
		int cnt=0;
		for(int i=0,j;i<m;i=j){
			for(j=i+1;j<m&&e[i].u==e[j].u&&e[i].v==e[j].v;j++)
				e[i].w+=e[j].w;
			e[cnt++]=e[i];
		}
		//建圖
		sqr=(int)sqrt(cnt<<1);
		memset(du,0,sizeof(du));
		F(i,0,cnt-1)du[e[i].u]++,du[e[i].v]++;
		F(i,1,n)sp[i]=(du[i]>=sqr);
		memset(g,0,sizeof(g)),ed=0;
		F(i,0,cnt-1){
			int x=e[i].u,y=e[i].v;LL w=e[i].w;
			//重點是被訪問的點,輕點是要訪問所有的點
			if(sp[x])adg(1,y,x,w);else adg(0,x,y,w);
			if(sp[y])adg(1,x,y,w);else adg(0,y,x,w);
		}
		memset(ans,0,sizeof(ans)),memset(sum,0,sizeof(sum));
		//維護每一種ans和重點周圍的邊的權值和
		F(i,0,cnt-1){
			int x=e[i].u,y=e[i].v;LL w=e[i].w;
			if(sp[x])sum[x][type[y]]+=w;
			if(sp[y])sum[y][type[x]]+=w;
			ans[type[x]+type[y]]+=w;
		}
		printf("Case %d:\n", ic++);
		int q,a,b,x;char s[20];
		scanf("%d",&q);
		while(q--){
			scanf("%s",s);
			if(s[0]=='A')scanf("%d%d",&a,&b),printf("%lld\n",ans[a+b]);
			else{//修改點
				scanf("%d",&x);
				type[x]^=1;
				if(sp[x]){//修改重點
					F(i,0,1){//對於每一種sum將原來的減去,加上修改後的
						ans[(type[x]^1)+i]-=sum[x][i];
						ans[type[x]+i]+=sum[x][i];
					}
				}else{//修改輕點,直接暴力修改
					for(int i=g[x][0];i;i=nxt[i]){
						ans[(type[x]^1)+type[v[i]]]-=w[i];
						ans[type[x]+type[v[i]]]+=w[i]; 
					}
				}//更新重點的sum
				for(int i=g[x][1];i;i=nxt[i]){
					sum[v[i]][type[x]^1]-=w[i]; 
					sum[v[i]][type[x]]+=w[i];
				}
			}
		}
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章