bzoj3531/洛谷P3313 旅行 树剖+动态开点线段树

题目分析

【提示】您已获得新技能:动态开点线段树
这道题,如果没有只能留宿于信仰相同的城市这个规定,显然是用树剖瞎搞一通,再用线段树瞎搞一通。
现在有了宗教信仰的规定,很容易想到的一个思路是对于每个宗教开一棵线段树。但是这样空间会GG,所以又想到动态开点线段树。
所谓动态开点线段树,就是你的编号不能和普通线段树一样,x的儿子是 x2 x ∗ 2 x2+1 x ∗ 2 + 1 ,而应该新编号。除此之外,和普通线段树没有什么区别。
做完这道题后,可以去查查有关飞天面条神教,隐形独角兽教和绝地教的百度百科辣

代码

#include<bits/stdc++.h>
using namespace std;
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
const int N=100005,M=5000005;
int n,Q,tot,SZ,now;
int h[N],ne[N<<1],to[N<<1],c[N],w[N];
int top[N],sz[N],f[N],pos[N],dep[N],rt[N];
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
void dfs1(int x,int las) {//树剖
    f[x]=las,sz[x]=1,dep[x]=dep[las]+1;
    for(int i=h[x];i;i=ne[i])
        if(to[i]!=las) dfs1(to[i],x),sz[x]+=sz[to[i]];
}
void dfs2(int x,int las) {
    int mx=0,bj=0; pos[x]=++now;
    for(int i=h[x];i;i=ne[i])
        if(to[i]!=las&&mx<sz[to[i]]) mx=sz[to[i]],bj=to[i];
    if(!bj) return;
    top[bj]=top[x],dfs2(bj,x);
    for(int i=h[x];i;i=ne[i])
        if(to[i]!=las&&to[i]!=bj) top[to[i]]=to[i],dfs2(to[i],x);
}
int sum[M],mx[M],ls[M],rs[M];
void up(int x){
    sum[x]=sum[ls[x]]+sum[rs[x]],mx[x]=max(mx[ls[x]],mx[rs[x]]);
}
void chan(int &x,int s,int t,int mb,int num) {//动态加入新点
    if(!x) x=++SZ;//开个新点
    if(s==t) {sum[x]=mx[x]=num;return;}
    int mid=(s+t)>>1;
    if(mb<=mid) chan(ls[x],s,mid,mb,num);
    else chan(rs[x],mid+1,t,mb,num);
    up(x);
}
int getmx(int x,int l,int r,int s,int t) {
    if(l<=s&&t<=r) return mx[x];
    int mid=(s+t)>>1,re=0;
    if(l<=mid) re=getmx(ls[x],l,r,s,mid);
    if(mid+1<=r) re=max(re,getmx(rs[x],l,r,mid+1,t));
    return re;
}
int getsum(int x,int l,int r,int s,int t) {
    if(l<=s&&t<=r) return sum[x];
    int mid=(s+t)>>1,re=0;
    if(l<=mid) re=getsum(ls[x],l,r,s,mid);
    if(mid+1<=r) re+=getsum(rs[x],l,r,mid+1,t);
    return re;
}
void work(int x,int y,int flag) {//获得答案
    int re=0,cx=c[x];
    while(top[x]!=top[y]) {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        if(flag) re=max(re,getmx(rt[cx],pos[top[x]],pos[x],1,n));
        else re+=getsum(rt[cx],pos[top[x]],pos[x],1,n);
        x=f[top[x]];
    }
    if(pos[x]>pos[y]) swap(x,y);
    if(flag) re=max(re,getmx(rt[cx],pos[x],pos[y],1,n));
    else re+=getsum(rt[cx],pos[x],pos[y],1,n);
    printf("%d\n",re);
}
int main()
{
    int x,y;char ch[10];
    n=read(),Q=read();
    for(int i=1;i<=n;++i) w[i]=read(),c[i]=read();
    for(int i=1;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
    dfs1(1,0),top[1]=1,dfs2(1,0);
    for(int i=1;i<=n;++i) chan(rt[c[i]],1,n,pos[i],w[i]);
    while(Q--) {
        scanf("%s",ch),x=read(),y=read();
        if(ch[0]=='C'&&ch[1]=='C')
            chan(rt[c[x]],1,n,pos[x],0),c[x]=y,chan(rt[y],1,n,pos[x],w[x]);
        else if(ch[0]=='C'&&ch[1]=='W')
            w[x]=y,chan(rt[c[x]],1,n,pos[x],w[x]);
        else if(ch[0]=='Q'&&ch[1]=='S') work(x,y,0);
        else work(x,y,1);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值