2019西北工业大学程序设计创新实践基地春季选拔赛(重现赛) --Chino with Rewrite(树链剖分)

5 篇文章 0 订阅

链接:https://ac.nowcoder.com/acm/contest/553/I
来源:牛客网
 

Chino的数学很差,因此Cocoa非常担心。这一天,Cocoa准备教Chino如何出题。
«Rewrite»是Key社的一款非常好玩的文字冒险游戏,在游戏的背景设定里,かがり需要完成一棵世界树的重构,重写这个世界。
这很像出题呢,一开始只有几个零星的idea,慢慢地把它们组织到一起,变成了一道完整的题目。
Cocoa告诉Chino应该按照如下的方式出题:
1、起初,平面上只有个知识点,每个知识点都有一个权值,接下来LoliconAutomaton准备通过次操作将它们连成一棵知识树(我们姑且称作“智慧树”):
2、操作“1xy”表示了直接连接x,y两个知识点,并把它们的权值更新为,如果x,y已经直接或者间接地连通,忽略本次操作。其中表示“x向下取整”,例如.
3、操作“2 xy”询问的唯一路径上的知识点有几种不同的权值,如果x,y当前尚未连通,请输出-1.
现在Cocoa希望Chino能够回答每一个“2xy”.
题目对Chino来说太难啦,你能帮一帮Chino吗?

输入描述:

第一行是两个正整数n, q;接下来一行是n个正整数Wi;接下来n-1行每行两个数u, v,描述了树上的一条边;接下来q行每行描述了一组询问

输出描述:

对于每个2 x y,你应该作出回答

示例1

输入

复制

5 8
3 2 7 6 7
1 1 2
1 1 3
2 2 3
1 2 4
2 1 5
1 2 5
1 1 5
2 3 5

输出

复制

2
-1
2

题意:给你N个独立的点,每个点有一个1到60的权值,给你Q次操作,有加边和查询两种操作,加边是将原先俩个不连通的边联通,值变为平均值向下取整,如果已经联通就无视操作,查询是询问两点的唯一路径上权值不同的个数;

题解

    Q次操作完后,点变成了一棵数,点到点之间的路径是唯一的,那其实当两个点联通后,无论你在怎么进行加边操作都只能改变路上节点对应的权值而不会改变路径。

   通过离线处理将树建立后,每一次加边操作就是改变对应节点的权值,而权值是1到60的数,long long 是64位大整数就可以用大整数在查询是对答案进行状压处理。

对于询问的两点如果是在树的不同链上,那么他们肯定会有一个公共的父亲节点也就是lca,查询的路径就是两点到lca的路径;对于在同一链上的节点,查询的路径就是两点之间的路径;

那么现在就变成了对点到链进行修改然后跑两点的查询路径求lca,就变成了树链剖分的模板题

AC代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<set>
#include<vector>
#include<math.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int wi[maxn],ans[maxn*3],num[maxn][65],n;
struct node{
    int l,r,id;
};
node y[maxn*3];
vector<int> v[maxn];
int father[maxn];
int son[maxn],in[maxn],fa[maxn],id[maxn];
int si[maxn],ti=0,deep[maxn],fa1[maxn];
ll tree[maxn*4];
int find1(int x){
    if(x==father[x])
        return x;
    return father[x]=find1(father[x]);
}
void dfs(int x,int f){
    si[x]=1;
    deep[x]=deep[f]+1;
    //printf("%d %d %d %d\n",x,f,deep[x],deep[f]);
    fa1[x]=f;
    int son1=0;
    son[x]=0;
    for(int a=0;a<v[x].size();a++){
        int b=v[x][a];
        if(b==f)
            continue;
        dfs(b,x);
        si[x]+=si[b];
        if(si[b]>son1){
            son1=si[b];
            son[x]=b;
        }
    }
}
void dfs1(int x,int f,int top){
    id[x]=++ti;
    fa[x]=top;
    if(son[x]!=0)
        dfs1(son[x],x,top);
    for(int a=0;a<v[x].size();a++){
        int b=v[x][a];
        if(b==f||b==son[x])
            continue;
        dfs1(b,x,b);
    }
}
void add(int l1,int r1,int k,int m,ll an){
    if(l1==r1){
        tree[k]=an;
        return ;
    }
    int mid=(l1+r1)>>1;
    if(mid>=m)
        add(l1,mid,2*k,m,an);
    if(mid+1<=m)
        add(mid+1,r1,2*k+1,m,an);
    tree[k]=tree[2*k]|tree[2*k+1];
}
ll query(int l1,int r1,int l2,int r2,int k){
    ll ans=0;
    if(l1>=l2&&r1<=r2){
        ans|=tree[k];
        return ans;
    }
    int mid=(l1+r1)>>1;
    if(mid>=l2)
        ans|=query(l1,mid,l2,r2,2*k);
    if(mid+1<=r2)
        ans|=query(mid+1,r1,l2,r2,2*k+1);
    return ans;
}
int get(ll ans){
    int an=0;
    for(int a=0;a<=60;a++){
        int b=ans&1;
        ans>>=1;
        if(b==1)
            an++;
    }
    return an;
}
int work(int i,int j){
    ll ans=0;
    if(i==j)
        return get(query(1,n,id[fa[i]],id[fa[j]],1));
    //printf("%d %d %d %d\n",deep[i],deep[j],i,j);
    while(fa[i]!=fa[j]){
        if(deep[fa[i]]>deep[fa[j]]){
            ans|=query(1,n,id[fa[i]],id[i],1);
            i=fa1[fa[i]];
        }
        else{
            ans|=query(1,n,id[fa[j]],id[j],1);
            j=fa1[fa[j]];
        }
        //printf("%lld\n",ans);
    }
    if(id[i]<id[j])
        swap(i,j);
    ans|=query(1,n,id[j],id[i],1);
    return get(ans);
}
int main( )
{
    int m=0;
    int q;
    scanf("%d %d",&n,&q);
    for(int a=1;a<=n;a++){
        scanf("%d",&wi[a]);
        father[a]=a;
    }
    for(int a=1;a<=q;a++){
        scanf("%d %d %d",&y[a].id,&y[a].l,&y[a].r);
        int i=find1(y[a].l);
        int j=find1(y[a].r);
        if(y[a].id==1){
            if(i==j)
                continue;
            v[y[a].l].push_back(y[a].r);
            v[y[a].r].push_back(y[a].l);
            father[i]=j;
        }
    }
    deep[0]=0;
    dfs(1,0);
    dfs1(1,0,1);
    for(int a=1;a<=n;a++)
        father[a]=a;
    for(int a=1;a<=q;a++){
        int i=find1(y[a].l);
        int j=find1(y[a].r);
        if(y[a].id==1){
            if(i==j)
            continue;
            int z=(wi[y[a].l]+wi[y[a].r])/2;
            add(1,n,1,id[y[a].l],(ll)1<<z);
            add(1,n,1,id[y[a].r],(ll)1<<z);
            wi[y[a].l]=wi[y[a].r]=z;
            father[i]=j;
        }
        else{
            if(i!=j){
                printf("-1\n");
                continue;
            }
            printf("%d\n",work(y[a].l,y[a].r));
        }
    }
}
/*5 6
1 2 3 1 1
1 1 2
1 2 3
2 1 3
1 3 4
1 2 5
2 1 3*/

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值