某模拟赛 T1 strength【根号分治】

(20200505)

题意

一张有 N N N 个点的图,每个点有黑白之一的颜色,其中 M M M 条边初始时就有权值,其余各点之间权值默认为 0。

Q Q Q 次操作操作:

  • 更改某点的颜色;
  • 更改某两个点之间的权值;
  • 询问所有颜色不相同的点对之间的边权之和( a n s = ∑ i = 1 n ∑ j = 1 n [ c o l i ≠ c o l j ] w i j ans=\sum\limits_{i=1}^n\sum\limits_{j=1}^n[col_i\ne col_j]w_{ij} ans=i=1nj=1n[coli=colj]wij)。

1 ≤ N , M , Q ≤ 1 0 5 1\leq N,M,Q\leq 10^5 1N,M,Q105

题解

首先考虑暴力:

记录目前的 a n s ans ans、每个点的颜色 c o l i col_i coli

更改两点之间权值时根据两点颜色异同更改 a n s ans ans

更改某点颜色时有两种思路:

  • 枚举该点连接的所有边,根据各点与该点颜色异同加减 a n s ans ans
  • 维护每个点与其同/异色点之间的边权之和 f i , e i f_i,e_i fi,ei,则 a n s ← a n s − e i + f i ans\gets ans-e_i+f_i ansansei+fi。但是为了维护其他边的 f i , e i f_i,e_i fi,ei 仍需枚举该点连接的所有边。

于是考虑根号分治:

  • 度数大于 n \sqrt{n} n 的点,称之为 大点,维护其 f i , e i f_i,e_i fi,ei
    • 更改颜色时,直接修改 a n s ans ans,并维护与之相连的大点(至多 O ( n ) O(\sqrt{n}) O(n ) 个)的 f i , e i f_i,e_i fi,ei
  • 度数不大于 n \sqrt{n} n 的点,称之为 小点,不维护其 f i , e i f_i,e_i fi,ei
    • 更改颜色时,枚举该点连接的所有边(至多 n \sqrt{n} n 条),根据各点与该点颜色异同加减 a n s ans ans,并顺便维护与之相连的大点的 f i , e i f_i,e_i fi,ei

综上,时间复杂度为 O ( N N ) O(N\sqrt{N}) O(NN )(忽略 N , M , Q N,M,Q N,M,Q 的差别)。

代码(代码中没有 为了不计算小点的 f i , e i f_i,e_i fi,ei 而专门处理):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fi first
#define se second
#define mp make_pair
#define mit map<int,int>::iterator
int getint(){
	int ans=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		ans=ans*10+c-'0';
		c=getchar();
	}
	return ans*f;
}
const int N=1e5+10;

map<int,int> to[N];
map<int,int> bigs[N];
bool col[N],big[N];
ll f[N],e[N];
ll ans=0;

int main(){
	int n=getint(),m=getint(),q=getint();
	int sqrtn=sqrt(n);
	for(int i=1;i<=n;i++)col[i]=getint();
	for(int i=0;i<m;i++){
		int x=getint(),y=getint(),z=getint();
		to[x][y]=z;
		to[y][x]=z;
		if(col[x]!=col[y])ans+=z,e[x]+=z,e[y]+=z;
		else f[x]+=z,f[y]+=z;
	}
	for(int i=1;i<=n;i++){
		big[i]=(to[i].size()>sqrtn);
	}
	for(int i=1;i<=n;i++){
		for(mit j=to[i].begin();j!=to[i].end();++j){
			if(big[j->fi])bigs[i][j->fi]=j->se;
		}
	}
	while(q--){
		int op=getint();
		if(op==1){
			int x=getint();
			if(big[x]){
				for(mit i=bigs[x].begin();i!=bigs[x].end();++i){
					if(col[i->fi]==col[x]) f[i->fi]-=i->se,e[i->fi]+=i->se;
					else f[i->fi]+=i->se,e[i->fi]-=i->se;
				}
			}else{
				f[x]=e[x]=0;
				for(mit i=to[x].begin();i!=to[x].end();++i){
					if(col[i->fi]==col[x]) f[i->fi]-=i->se,e[i->fi]+=i->se, f[x]+=i->se;
					else f[i->fi]+=i->se,e[i->fi]-=i->se, e[x]+=i->se;
				}
			}
			ans=ans+f[x]-e[x];
			swap(e[x],f[x]);
			col[x]=!col[x];
		}
		if(op==2){
			int x=getint(),y=getint(),z=getint();
			int w=to[x][y];
			if(col[x]!=col[y]){
				ans=ans-w+z;
			}
			to[x][y]=to[y][x]=z;
			if(big[x]){
				if(col[x]==col[y])f[x]=f[x]-w+z;
				else e[x]=e[x]-w+z;
				bigs[y][x]=z;
			}
			if(big[y]){
				if(col[x]==col[y])f[y]=f[y]-w+z;
				else e[y]=e[y]-w+z;
				bigs[x][y]=z;
			}
		}
		if(op==3){
			printf("%lld\n",ans);
		}
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值