题意:给出n个点,m条边,每个点都有一个颜色,黑色(用1表示)或白色(用0表示)。有两种操作,一种是询问所有边中满足边的两个点是00,01 or 11这三种情况的边的权值的和,另一种是修改某一点的颜色。
思路:这一题最直观的思路就是暴力,查询简单,用一个数组预处理三种情况的边就能在O(1)的时间内完成了,修改一个点u的话,再遍历所有与u相连的点,把数组更新就好了。但是这样还是有问题的,由于m比较大,最坏的情况,修改一个点的时间复杂的有可能高达O(m),这样明显是不行的。为了减少这种情况的出现,我们要进行一些优化,简单的来说,就是把度数非常大的点单独挑出来,然后额外处理这些点,其他点还是暴力搞。那么挑哪些点才合适呢?其实只要选择那些度数>sqrt(m)的点就行了,因为这些点的个数不会超过2*sqrt(m),这个数不会超过700,所以每次操作不会超过700,还是可以接受的。这里证明一下吧,设度数大于sqrt(m)的点的个数为cnt,则cnt*sqrt(m)<=m*2,所以cnt<=sqrt(m)*2。处理这些度数大的点可以用一个数组a[i][j]记录每两个点之间的距离(如果没有边设成0就好了),用sum[i][0]和sum[i][1]分别记录与这个点相连的边中另一个顶点为白(黑)色点的总权值。最后只要注意一下处理重边就行了,重边要合并在一起,不然会添加不必要的节点,会超时。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
struct Edge
{
int u,v,next;
ll w;
};
Edge edges[maxn<<1];
int head[maxn],nEdge;
int n,m;
int color[maxn],degree[maxn];
ll a[700][700],sum[700][2];
ll res[3];
int hash[maxn],spnt;
void AddEdge(int u,int v,int w)
{
nEdge++;
edges[nEdge].u=u;
edges[nEdge].v=v;
edges[nEdge].w=w;
edges[nEdge].next=head[u];
head[u]=nEdge;
}
void Init()
{
memset(head,0xff,sizeof(head));
memset(degree,0,sizeof(degree));
memset(hash,0,sizeof(hash));
memset(res,0,sizeof(res));
nEdge=-1;spnt=0;
}
void precal()
{
int lim=(int)sqrt((double)m);
for(int i=1;i<=n;++i)
{
if(degree[i]>lim)
{
spnt++;
hash[i]=spnt;
}
}
memset(a,0,sizeof(a));
memset(sum,0,sizeof(sum));
int u,v;
for(int i=0;i<nEdge;i+=2)
{
u=edges[i].u;v=edges[i].v;
if(color[u]^color[v])
res[2]+=edges[i].w;
else if(color[u])
res[1]+=edges[i].w;
else res[0]+=edges[i].w;
if(hash[u]&&hash[v])
{
a[hash[u]][hash[v]]+=edges[i].w;
a[hash[v]][hash[u]]+=edges[i].w;
}
if(hash[u]) sum[hash[u]][color[v]]+=edges[i].w;
if(hash[v]) sum[hash[v]][color[u]]+=edges[i].w;
}
}
void change(int x)
{
if(hash[x])
{
if(color[x])
{
res[0]+=sum[hash[x]][0];
res[2]+=sum[hash[x]][1];
res[2]-=sum[hash[x]][0];
res[1]-=sum[hash[x]][1];
}
else
{
res[1]+=sum[hash[x]][1];
res[2]+=sum[hash[x]][0];
res[2]-=sum[hash[x]][1];
res[0]-=sum[hash[x]][0];
}
for(int i=1;i<=spnt;++i)
{
if(i==hash[x]) continue;
sum[i][color[x]]-=a[i][hash[x]];
sum[i][color[x]^1]+=a[i][hash[x]];
}
color[x]^=1;
}
else
{
int v;
for(int k=head[x];k!=-1;k=edges[k].next)
{
v=edges[k].v;
if(color[x])
{
if(color[v]) res[2]+=edges[k].w,res[1]-=edges[k].w;
else res[0]+=edges[k].w,res[2]-=edges[k].w;
}
else
{
if(color[v]) res[1]+=edges[k].w,res[2]-=edges[k].w;
else res[2]+=edges[k].w,res[0]-=edges[k].w;
}
if(hash[v])
{
sum[hash[v]][color[x]]-=edges[k].w;
sum[hash[v]][color[x]^1]+=edges[k].w;
}
}
color[x]^=1;
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int tcase=0;
int q;
char str[10];
map<ll,int>mp;
while(~scanf("%d%d",&n,&m))
{
tcase++;
Init();
mp.clear();
for(int i=1;i<=n;++i)
scanf("%d",&color[i]);
int u,v,w,id;
ll tmp;
for(int i=0;i<m;++i)
{
scanf("%d%d%d",&u,&v,&w);
if(u>v) swap(u,v);
tmp=(ll)u*100000+v;
if(!mp[tmp])
{
degree[u]++;degree[v]++;
AddEdge(u,v,w);
AddEdge(v,u,w);
mp[tmp]=nEdge;
}
else
{
id=mp[tmp];
edges[id].w+=w;
edges[id^1].w+=w;
}
}
precal();
printf("Case %d:\n",tcase);
scanf("%d",&q);
int a,b;
while(q--)
{
scanf("%s",str);
if(str[0]=='A')
{
scanf("%d%d",&a,&b);
if(a^b) printf("%I64d\n",res[2]);
else printf("%I64d\n",res[a]);
}
else
{
scanf("%d",&a);
change(a);
}
}
}
return 0;
}