题目大意:给一张无向图,有n个点,m条边,每个点2个状态:0和1,每条边有一个权值w,现在给q个操作。有2种操作,change x表示改变x点的状态。asksum a,b表示查询所有端点为a 和 b的边的权值之和。
题目分析:这题很容易想到暴力枚举所有边统计求和的方法。比赛的时候实际也是这么做的。这么做的复杂度O(q*m),显然是不能承受的。比赛的时候一直以为是某种高深的数据结构,结果看了题解才发现是构造。想法其实也不难想,只是不敢想。
因为暴力的话,时间主要消耗在边的遍历上,那么如何减少遍历边的时间呢。很容易想到对度比较大的节点特殊处理,但具体如何处理,实在是想不出来,太弱了啊。。。
重新分析一下此题:由于每个点只有0 1两个状态,那么答案只有3种情况,用一个数组维护即可。即sum[0]统计边的两端都是0的权值和,sum[1]统计边的两端为1和0的权值总和,sum[2]统计边的两端都是1的权值和。
那么只要维护好这个数组,那么对于查询操作,可以O(1)的时间输出。那么关键就在change操作的维护了。
仔细分析可知,每次change操作只改变一个点的状态,那么与这个点有边相连的点的状态是不会发生改变的。我们在每个点上维护2个值:ans[0]维护与当前顶点相连且状态为0的权值和,ans[1]维护与当前顶点相连且状态为1的权值和。每次改变当前顶点的状态后,其他的点状态是不变的,那么这些权值和也是不变的。而每次改变的是与当前顶点相连的点的ans值,因为当前顶点的状态发生了改变。而总的sum数组也要随着一个点的状态的改变而改变。而这个改变其实很简单:
设要改变状态的点为x,先要从sum[color[x] + 0]里面减去x.ans[0],因为x改变状态之前,x.ans[0]被加进了sum[color[x] + 0]里面(想一想,为什么),那么现在x改变了状态,所以要先把x.ans[0]减掉,转而加到sum[color[x]^1+0]里面,即加到转换状态后应该加的地方。sum[color[x] + 1]同理。
说了半天还没有说到最关键的地方,如何降低复杂度。刚才说了统计每个点的度数,那么我们把所有的点分成两类,第一类度数小于sqrt(m)(为什么是sqrt(m),这个其实不一定非要这么多,这只是一个大概的分类,因为可以证明,当边为m的时候,无向图中点的度数大于sqrt(m)的点不超过2*sqrt(m)个)。这样的话,我们可以在建邻接表的时候,对于普通点,将所有与之邻接的点都加入邻接表,直接暴力更新即可,对于超级点,我们可以忽略与之相邻的普通点,只需要将与之相邻的超级点加入邻接表即可。因为我们change的时候对每个普通点是暴力遍历的,每次重新统计即可,但是对于超级点,我们只遍历与之相邻的超级点,维护相应的ans,这样大大节省了时间。
也许描述很抽象,最后一点也是刚走路的时候才想同。给大家个例子,大家不妨在纸上划划,就会明白的,为什么可以无视与超级点直接相连的普通点。
例子:一个超级点,度数为n,与n个普通点相连。这样建邻接表的时候,超级点是没有相邻点的,随便更改状态,跑出来的结果是不会错的,对于超级点,ans[0],ans[1]是不会变的,对于普通点,不管超级点怎么变,反正每次都要重新统计,不会出错的。
这样一来,原来的复杂度就变成了O(q*sqrt(m)),是可以接受的。
还有2点要注意的:重边要合并!!!!边权值要用__int64!!!此处WA数次!!!
详情请见代码:
#include
#include
#include
#include
using namespace std;
const int N = 100005;
typedef __int64 ll;
ll sum[3];
int cl[N],de[N];
const int lim = 350;
/*
330 2140MS 8456K
300 2156MS 8456K
*/
struct nd
{
int a,b;
ll val;//!!!!
}edge[N];
struct spn
{
bool flag;
ll ans[2];
}supernode[N];
int cmp(struct nd ta,struct nd tb)
{
if(ta.a != tb.a)
return ta.a < tb.a;
else
return ta.b < tb.b;
}
struct adj
{
int to,next;
ll val;//!!!!
}g[N + N];
int num;
int head[N];
int m,n,q;
void build(int s,int e,ll v)
{
g[num].to = e;
g[num].val = v;
g[num].next = head[s];
head[s] = num;
num ++;
}
void change(int x)
{
int i;
if(supernode[x].flag)//super node
{
for(i = head[x];i != -1;i = g[i].next)
{
supernode[g[i].to].ans[cl[x]] -= g[i].val;
supernode[g[i].to].ans[cl[x]^1] += g[i].val;
}
}
else//非super node
{
supernode[x].ans[0] = supernode[x].ans[1] = 0;//每次要重新统计!!
for(i = head[x];i != -1;i = g[i].next)
{
supernode[x].ans[cl[g[i].to]] += g[i].val;
if(supernode[g[i].to].flag)
{
supernode[g[i].to].ans[cl[x]] -= g[i].val;
supernode[g[i].to].ans[cl[x]^1] += g[i].val;
}
}
}
/*其实上面的if else完全去掉,只加上这一段也是能AC的,但是不严谨,因为
为了节省时间,我们在处理超级点的时候没有考虑与之相连的普通点,那么我们在
暴力遍历普通点的时候,是要重新统计的,因为超级点状态改变了,与之相连的
普通点也应该改变的,我们没有变,为了省时间,但如果我们有机会遍历该普通点
应该想办法弥补的,弥补的办法就是重新统计!!
for(i = head[x];i != -1;i = g[i].next)
{
supernode[g[i].to].ans[cl[x]] -= g[i].val;
supernode[g[i].to].ans[cl[x]^1] += g[i].val;
}
*/
sum[cl[x]] -= supernode[x].ans[0];
sum[cl[x] + 1] -= supernode[x].ans[1];
cl[x] ^= 1;
sum[cl[x]] += supernode[x].ans[0];
sum[cl[x] + 1] += supernode[x].ans[1];
}
int main()
{
int i;
int a,b;
int cas = 0;
while(scanf("%d%d",&n,&m) != EOF)
{
memset(sum,0,sizeof(sum));
for(i = 1;i <= n;i ++)
{
scanf("%d",&cl[i]);
head[i] = -1;
de[i] = 0;
supernode[i].flag = false;
memset(supernode[i].ans,0,sizeof(supernode[i].ans));
}
for(i = 1;i <= m;i ++)
{
scanf("%d%d%I64d",&edge[i].a,&edge[i].b,&edge[i].val);
if(edge[i].a > edge[i].b)
edge[i].a ^= edge[i].b ^= edge[i].a ^= edge[i].b;
}
sort(edge + 1,edge + m + 1,cmp);
int t = 1;//离散化 去重边
for(i = 2;i <= m;i ++)
{
if(edge[i].a == edge[t].a && edge[i].b == edge[t].b)
edge[t].val += edge[i].val;
else
edge[++t] = edge[i];
}
m = t;
for(i = 1;i <= m;i ++)//统计度数 找出超级点
{
de[edge[i].a] ++;
de[edge[i].b] ++;
sum[cl[edge[i].a] + cl[edge[i].b]] += edge[i].val;
}
for(i = 1;i <= n;i ++)
{
if(de[i] >= lim)//super node
supernode[i].flag = true;
if(de[i] >= lim)
supernode[i].flag = true;
}
num = 0;
for(i = 1;i <= m;i ++)//普通点建邻接表,超级点维护ans
{
int ta = edge[i].a;
int tb = edge[i].b;
supernode[ta].ans[cl[tb]] += edge[i].val;
supernode[tb].ans[cl[ta]] += edge[i].val;
if(supernode[ta].flag && supernode[tb].flag)//
{
build(ta,tb,edge[i].val);
build(tb,ta,edge[i].val);
}
if(supernode[ta].flag == false)
build(ta,tb,edge[i].val);
if(supernode[tb].flag == false)
build(tb,ta,edge[i].val);
}
scanf("%d",&q);
printf("Case %d:\n",++cas);
char op[20];
while(q --)
{
scanf("%s",op);
if(op[0] == 'A')
{
scanf("%d%d",&a,&b);
printf("%I64d\n",sum[a + b]);
}
else
{
scanf("%d",&a);
change(a);
}
}
}
return 0;
}
//1625MS 8456K
//1578MS 8456K