树链剖分

【问题描述】

树链剖分可以干什么?
“可以支持在树中快速修改一个点信息,快速询问一条链信息”
LCT 可以干什么?
“可以支持树链剖分支持的特性,并且支持快速链接两个棵树,或者断开某条边”
那我现在要出一道关于树的题目,一开始有 n 个点,每个点自成一颗树,所以现在有 n 棵树。
每个点有一个权值。有以下这些操作类型:
连接操作:1 a b,连接 a 和 b,使 a 成为 b 的儿子结点,a 一定是某个树的根节点。这
样就把两个数连接到一起,此时 a 以及所有后代的根节点均为 b 所在树的根节点。
询问操作:2 a,询问 a 到自己所在树的根节点所有在路径(包括自己和根节点)上的
所有点的权值的异或和。
恩…但是由于我懒,所以还没有造数据,请你帮我写个标程让我用来造数据吧。

【输入】

第一行两个整数 n 和 m,表示有 n 个点和 m 个操作。
接下有一行 n 个整数,第 i 个整数表示第 i 个点的权值。
接下来 m 行,每行为三个整数 1 a b,或者 2 a。
如果是 1 a b 表示这是一个连接操作,意义见题目。
如果是 2 a 表示这是一个询问操作,意义见题目。

【输出】

对于每个询问操作,输出一行,表示从 a 到根节点路径上所有点的权值的异或和。

【输入输出样例 1】

Input
5 8
1 2 3 4 5
2 2
1 2 1
2 2
1 4 3
1 3 2
2 3
1 5 1
2 5
Output
2
3
0
4

样例解释

样例中每个节点权值与标号相同
第一个询问中 2 的根节点是自己,所以第一个询问 2 节点的答案为 0
第二个询问中 2 的根节点是 1,路径为 2-1,所以答案为 2 xor 1 = 3
第三个询问中 3 到根节点的路径为 3-2-1,所以答案为 3 xor 2 xor 1 = 0
第四个询问中 5 到根节点的路径为 5-1,所以答案为 5 xor 1 =4

数据范围

对于 40%的数据 1 <= n,m <= 1000
对于 90%的数据 1 <= n <= 100000, 1 <= m <= 200000
对于 100%的数据 1 <= n <= 300000, 1 <= m <= 500000
所有节点的权值均为正整数且在 int 范围内

【题解】

40%
暴力

100%
异或和只是到父亲的路径上的,所以在路径压缩的同时就可以维护。每次合并则把接头处异或一下;每次路径压缩都合并一下自身和父亲的异或和,再异或一下父亲去重(利用了两次异或变回1的性质),这就是维护加权并查集附加信息的特殊操作了。查询时再find一次,确保输出的是最新结果。

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fp(i,a,b) for(int i=a;i<=b;i++)
#define fq(i,a,b) for(int i=a;i>=b;i--)
#define il inline
#define ll long long
#define re register
using namespace std;
int n,m,w[300005]={},f[300005]={},dis[300005]={};//w代表点权
il int gi()
{
   int x=0;
   short int t=1;
   char ch=getchar();
  while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
int find(int x)//加权并查集
{
    int a,b;
    if(f[x]==x||f[x]==f[f[x]]) return f[x];
    a=f[x];b=find(f[x]);
    dis[x]^=dis[a];//答案先除掉到当前点的异或值
    return f[x]=b;//更改祖先,再去异或到根的结果
}
int query(int x)
{
    int y=find(x);
    if(f[x]==x) return dis[x];//如果是根节点,直接返回根节点值
    else return dis[x]^dis[y];//否则最后异或一下根节点去重
}
int main()
{
    freopen("td.in","r",stdin);
    freopen("td.out","w",stdout);
    n=gi();m=gi();
    fp(i,1,n) f[i]=i,w[i]=dis[i]=gi();
    fp(i,1,m)
    {
        int c=gi(),u,v;
        if(c==1) u=gi(),v=gi(),f[u]=v;
        else u=gi(),printf("%d\n",query(u));
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

当然,gzy大佬提出了另一种思想:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
    int sum=0,p=1;char ch=getchar();
    while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
    if(ch=='-')p=-1,ch=getchar();
    while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
    return sum*p;
}
const int maxn=3e5+20;
int n,m,w[maxn],fa[maxn],dist[maxn];
int qu[maxn*2],cnt_qu,qu_root[maxn*2],faa[maxn];

struct node{
    int v,next;
};
node e[maxn*2];
int cnt,start[maxn];
void addedge(int u,int v)
{
    e[++cnt]=(node){v,start[u]};
    start[u]=cnt;
}

inline int fin(int x)
{
    return x==fa[x]?x:fa[x]=fin(fa[x]);
}

void init()
{
    n=read();m=read();
    REP(i,1,n)w[i]=read();
    REP(i,1,n)fa[i]=i;
    REP(i,1,m)
    {
        int type=read();
        if(type==1)
        {
            int v=read(),u=read();
            //fa[v]=u;
            int x=fin(v),y=fin(u);
            fa[x]=y;
            faa[v]=u;
            addedge(u,v);
        }else
        {
            int x=read();
            qu[++cnt_qu]=x;
            qu_root[cnt_qu]=fin(x);
        }
    }
}

int vis[maxn];

void dfs(int u)
{
    vis[u]=1;
    EREP(i,u)
    {
        int v=e[i].v;
        dist[v]=dist[u]^w[v];
        dfs(v);
    }
}

void doing()
{
    REP(i,1,n)
    {
        if(!vis[i])
        {
            int x=i;
            while(x!=fa[x])x=fa[x];
            dist[x]=w[x];
            dfs(x);
        }
    }
    REP(i,1,cnt_qu)
    {
        int x=qu[i],x_root=qu_root[i];
        cout<<(dist[x]^dist[faa[x_root]])<<endl;
    }
}

int main()
{
    freopen("td.in","r",stdin);
    freopen("td.out","w",stdout);
    init();
    doing();
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值