(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=1∑nj=1∑n[coli=colj]wij)。
1 ≤ N , M , Q ≤ 1 0 5 1\leq N,M,Q\leq 10^5 1≤N,M,Q≤105
题解
首先考虑暴力:
记录目前的 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 ans←ans−ei+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;
}