题意:
给出一颗树,树上有三种权值的边。
给出m次修改以及询问
每次修改将某条边的权值-1(1则不变)
定义从 能走到 的条件是,在从 到 的路径上,遇到第一条权值为3的边后,之后只有权值为1的边。
询问有2个,首先询问能否从t走到s,其次询问有多少点能走到s
分析:
考场上再次看错题。。。。尼玛是棵树啊!!!!
后来发现错误,想到正解已经是最后30分钟了。。。没调出来GG
其实是树的话就很简单了。
首先,可以存一个点向上,只走权值为1的边能达到的最远点(设为 )。以及只走1或2,能到达的最远点(设为 )。(维护可以用并查集)
设一个点的父亲节点为
第一问
这样一来,询问的第一问就很简单了,只需要考虑3种情况:
,说明只经过1或2的边就能走到。
,说明从s向上只走1的边,直到第一个非1的边,再只走1或2的边就能到达t(即包含了经过权值为3的边的情况)
,说明从t向上走权值为1或2的边,直到第一个3的边,再只走1就能到达s(这种情况主要是对s为t的祖先而处理的,相对应的,2情况能处理t是s的祖先的情况,而这个不能。如果s和t没有祖先关系,这种情况其实和2等价)
第二问
然后就是如何维护第二问的答案。
这里又要用到上面两个并查集了。
换一种说法定义这两个并查集,无非就是:只经过1的边形成的联通块(
即为x所在并查集的根),只经过1或2的边形成的联通块(
即为x所在并查集的根)。
考虑所有能到达s的点需要满足的条件:必须是从s出发,只经过1的边,直到一条非1的边(可为2可为3)后,只经过1或2的边能到达的点。
那么就可以把答案分为2部分,一部分是只经过1或2的边能达到的点数,一部分先经过1的边再经过一条3的边后能到达的点数。
(其实说是这样说,但实现的时候却是两部分混在一起求的)
首先两个并查集分别维护一个值,第一个并查集(只有1的边)维护从它向下走,遇到第一个3的边后,只经过1或2的边能到达的点的个数(定义为
)。
第二个并查集(有1或2的边)维护它的集合大小(定义为
)。
这样一来,询问时,首先加上 ,
然后判断其向上第一条非1的边是否为3,如果是3,则加上经过那条边后到达的点 的 (这里其实是第二部分的答案,但因为不方便维护祖先的信息,所以在查询的时候求)。
然后再加上 即可。
连边(更新)的时候,设两个点分别为
如果是2的边(从3变为2的边),
则
减去
,
加上
,即表示原来(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);
else
union2(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 = 3
ans+=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();
}
}
}