HDU2018多校第四场部分题目

HDU2018多校第四场部分题目

C Problems on a Tree(hdu 6334)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6334

题解
打比赛的时候想歪了,一直往动态点分治的方向想了,不知道要怎么修改。
对于这个题,我们维护两种并查集,一种只连所有1的边,记为1集合,一种连所有1和2的边,记为2集合。
对于每次询问,第一问只要考虑x点的1集合和y点的2集合是否相连,或者反过来,或者x点和y点爱同一个2集合。
第二问,题目要求以x点为终点的起点数,就是x点所在的2集合的点数,记为size2[x],再加上它所相邻的点的1集合的点数,记为size1[x]。出于处理方便,size1[x]只记录叶子节点的,父亲节点的查询的时候再找一下就好了。
对于修改操作,如果是3改2,那么就把这两个点在2集合连起来,把size1和size2对应的改一下。
如果是2改1,那么就把两个1集合合并一下,也修改一下它父亲的size2就好了。

代码

#include<bits/stdc++.h>
#define N 100010
using namespace std;
int T,n,m,f1[N],f2[N],size1[N],size2[N];
int k,la[N],ff[N*2],pre[N],ans,tag,dep[N];
struct node{int a,b,c;}e[N*2];
map<int,int>w[N];

void add(int a,int b,int c)
{
  e[++k]=(node){a,b,c};ff[k]=la[a];la[a]=k;
  e[++k]=(node){b,a,c};ff[k]=la[b];la[b]=k;
} 

int find1(int x)
{
  if(f1[x]==x)return x;
  return f1[x]=find1(f1[x]);
}

int find2(int x)
{
  if(f2[x]==x)return x;
  return f2[x]=find2(f2[x]);
}

void merge1(int a,int b)
{
  a=find1(a);b=find1(b);
  f1[b]=a;size1[a]+=size1[b];
}

void merge2(int a,int b)
{
  a=find2(a);b=find2(b);
  f2[b]=a;size2[a]+=size2[b];
  size1[find1(pre[b])]-=size2[b];
  size1[find1(pre[a])]+=size2[b];
}

void dfs(int x)
{
  dep[x]=dep[pre[x]]+1;
  for(int a=la[x];a;a=ff[a])
  {
    if(e[a].b==pre[x])continue;
    pre[e[a].b]=x;size1[x]++;dfs(e[a].b);
    if(e[a].c<=1)merge1(x,e[a].b);
    if(e[a].c<=2)merge2(x,e[a].b);
  }
}

int main()
{
  int a,b,c,x,y;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d",&n,&m);k=0;
    memset(la,0,sizeof(la));
    memset(ff,0,sizeof(ff));
    memset(size1,0,sizeof(size1));
    memset(pre,0,sizeof(pre));
    for(int i=1;i<=n;i++)f1[i]=i,f2[i]=i,size2[i]=1;
    for(int i=1;i<n;i++)
      scanf("%d%d%d",&a,&b,&c),add(a,b,c),w[a][b]=w[b][a]=c;
    dfs(1);
    while(m--)
    {
      scanf("%d%d%d%d",&a,&b,&x,&y);
      w[a][b]--;w[b][a]--;tag=0;
      if(dep[a]>dep[b])swap(a,b);
      if(w[a][b]==1)merge1(a,b);
      else if(w[a][b]==2)merge2(a,b);
      if(find2(x)==find2(y))tag=1;
      else if(find1(pre[find2(y)])==find1(x))tag=1;
           else if(find2(y)==find2(pre[find1(x)]))tag=1;
      ans=size1[find1(x)]+size2[find2(x)];
      if(find2(x)!=find2(pre[find1(x)]))
        ans+=size2[find2(pre[find1(x)])];
      printf("%d %d\n",tag,ans);
    }
  }
  return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值