题意:
给出一颗树,树上有三种权值的边。
给出m次修改以及询问
每次修改将某条边的权值-1(1则不变)
定义从uu能走到 的条件是,在从uu到 的路径上,遇到第一条权值为3的边后,之后只有权值为1的边。
询问有2个,首先询问能否从t走到s,其次询问有多少点能走到s
分析:
考场上再次看错题。。。。尼玛是棵树啊!!!!
后来发现错误,想到正解已经是最后30分钟了。。。没调出来GG
其实是树的话就很简单了。
首先,可以存一个点向上,只走权值为1的边能达到的最远点(设为f1(x)f1(x))。以及只走1或2,能到达的最远点(设为f2(x)f2(x))。(维护可以用并查集)
设一个点的父亲节点为fa(x)fa(x)
第一问
这样一来,询问的第一问就很简单了,只需要考虑3种情况:
1、f2(s)=f2(t)1、f2(s)=f2(t),说明只经过1或2的边就能走到。
2、f2(fa(f1(s)))=f2(t)2、f2(fa(f1(s)))=f2(t),说明从s向上只走1的边,直到第一个非1的边,再只走1或2的边就能到达t(即包含了经过权值为3的边的情况)
3、f1(s)=f1(fa(f2(t)))3、f1(s)=f1(fa(f2(t))),说明从t向上走权值为1或2的边,直到第一个3的边,再只走1就能到达s(这种情况主要是对s为t的祖先而处理的,相对应的,2情况能处理t是s的祖先的情况,而这个不能。如果s和t没有祖先关系,这种情况其实和2等价)
第二问
然后就是如何维护第二问的答案。
这里又要用到上面两个并查集了。
换一种说法定义这两个并查集,无非就是:只经过1的边形成的联通块(f1(x)f1(x)即为x所在并查集的根),只经过1或2的边形成的联通块(f2(x)f2(x)即为x所在并查集的根)。
考虑所有能到达s的点需要满足的条件:必须是从s出发,只经过1的边,直到一条非1的边(可为2可为3)后,只经过1或2的边能到达的点。
那么就可以把答案分为2部分,一部分是只经过1或2的边能达到的点数,一部分先经过1的边再经过一条3的边后能到达的点数。
(其实说是这样说,但实现的时候却是两部分混在一起求的)
首先两个并查集分别维护一个值,第一个并查集(只有1的边)维护从它向下走,遇到第一个3的边后,只经过1或2的边能到达的点的个数(定义为siz1(f1(x))siz1(f1(x)))。
第二个并查集(有1或2的边)维护它的集合大小(定义为siz2(f2(x))siz2(f2(x)))。
这样一来,询问时,首先加上siz2(f2(x))siz2(f2(x)),
然后判断其向上第一条非1的边是否为3,如果是3,则加上经过那条边后到达的点yy的 (这里其实是第二部分的答案,但因为不方便维护祖先的信息,所以在查询的时候求)。
然后再加上siz1(f1(x))siz1(f1(x))即可。
连边(更新)的时候,设两个点分别为x,y(x为y的父亲)x,y(x为y的父亲)
如果是2的边(从3变为2的边),
则siz1(f1(x))siz1(f1(x))减去siz2(y)siz2(y),siz1(f1(fa(x)))siz1(f1(fa(x)))加上siz2(y)siz2(y),即表示原来(x,y)这条3的边没有了,要更新它能贡献的新的祖先的信息。
如果1的边(从2变为1的边),则直接并查集合并即可。
这里说一下题外话。我看了标程的实现。。发现有一堆用不着的东西。。。看来这标程的作者自己也没想清楚啊。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define y1 We_can_read_of_things_that_happened_5,000_years_ago_in_the_Near_East,_where_people_first_learned_to_write._But_there_are_some_parts_of_the_word_where_even_now_people_cannot_write._The_only_way_that_they_can_preserve_their_history_is_to_recount_it_as_sagas_--_legends_handed_down_from_one_generation_of_another._These_legends_are_useful_because_they_can_tell_us_something_about_migrations_of_people_who_lived_long_ago,_but_none_could_write_down_what_they_did._Anthropologists_wondered_where_the_remote_ancestors_of_the_Polynesian_peoples_now_living_in_the_Pacific_Islands_came_from._The_sagas_of_these_people_explain_that_some_of_them_came_from_Indonesia_about_2,000_years_ago.
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
int fa[MAXN],f1[MAXN],f2[MAXN],dep[MAXN];
int siz[MAXN],siz2[MAXN];
vector<int> a[MAXN],w[MAXN];
int get_fa1(int x){if(!f1[x])return x;f1[x]=get_fa1(f1[x]);return f1[x];
}
int get_fa2(int x){if(!f2[x])return x;f2[x]=get_fa2(f2[x]);return f2[x];
}
void union1(int x,int y){if(dep[x]>dep[y])swap(x,y);x=get_fa1(x);y=get_fa1(y);f1[y]=x;siz[x]+=siz[y];
}
void union2(int x,int y){if(dep[x]>dep[y])swap(x,y);x=get_fa2(x);y=get_fa2(y);f2[y]=x;siz2[x]+=siz2[y];siz[get_fa1(fa[y])]-=siz2[y];int xx=get_fa1(fa[x]);//PF("{%d %d %d %d %d}\n",x,y,xx,siz[x],siz[y]);if(xx)siz[xx]+=siz2[y];
}
void dfs(int x,int fax){dep[x]=dep[fax]+1;fa[x]=fax;for(int i=0;i<a[x].size();i++){if(a[x][i]==fax)continue;siz[x]++;dfs(a[x][i],x); }for(int i=0;i<a[x].size();i++){if(a[x][i]==fax)continue;if(w[x][i]==2)union2(x,a[x][i]);if(w[x][i]==1){union2(x,a[x][i]);union1(x,a[x][i]);}}
}
void update(int x,int y){x=get_fa1(x);y=get_fa1(y);if(get_fa1(x)!=get_fa1(y)){//if(dep[x]<dep[y])// swap(x,y);if(get_fa2(x)==get_fa2(y))union1(x,y);elseunion2(x,y);}
}
int n,m,u,v,s,t,val;
int main(){int T;SF("%d",&T);while(T--){SF("%d%d",&n,&m); for(int i=1;i<=n;i++)siz2[i]=1;for(int i=1;i<n;i++){SF("%d%d%d",&u,&v,&val);a[u].push_back(v);a[v].push_back(u);w[u].push_back(val);w[v].push_back(val); }dfs(1,0);for(int i=1;i<=m;i++){SF("%d%d%d%d",&u,&v,&s,&t); bool flag=0;update(u,v);if(get_fa2(s)==get_fa2(t)||get_fa1(fa[get_fa2(t)])==get_fa1(s)||get_fa2(fa[get_fa1(s)])==get_fa2(t))flag=1;//PF("{%d %d}\n",get_fa1(s),siz[get_fa1(s)]);int ans=siz2[get_fa2(s)]+siz[get_fa1(s)];if(get_fa2(s)!=get_fa2(fa[get_fa1(s)]))//val = 3ans+=siz2[get_fa2(fa[get_fa1(s)])];PF("%d %d\n",flag,ans);}for(int i=1;i<=n;i++){f2[i]=0;f1[i]=0; siz[i]=0;a[i].clear();w[i].clear();}}
}