2016ACM/ICPC Shengyang Online-1002 List wants to travel

题意:给定一棵n个节点的树,有两种询问:1.(a,b,c)把节点a到节点b上路径的权值改为c;2.(a,b)询问节点a到b路径上连续不同权值的个数(如1,2,3,3,1算4个)

题解:树链剖分+线段树统计区间权值的段数(好难想)

对于剖分后权值个数的统计,需要考虑区间连接处的权值。操作:对于点u,可以每次记录u的儿子到u那条边的权值(没有儿子则为-1),线段树取区间时同时记录区间两端点的权值大小,比较,相同则答案-1,不同则答案不变。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N=40000+10;
const int E=100000+10;

int d[N],Fa[N],Size[N],Son[N],p[N],Top[N],pos;
struct Node{
    int cl,cr,m;
}T[N<<2];
int cL,cR;
int n,m;
int Edge[N][3],vp[N];
vector<int>V[N],W[N];
void dfs1(int u,int Pre,int Dep)
{
    d[u]=Dep+1;
    Fa[u]=Pre;
    Size[u]=1;
    for (int i=0;i<int(V[u].size());i++){
        int v=V[u][i];
        if (v!=Pre){
            dfs1(v,u,Dep+1);
            Size[u]+=Size[v];
            if (Son[u]==-1 || Size[Son[u]]<Size[v])Son[u]=v;
        }
    }
}
void dfs2(int u,int sp)
{
    Top[u]=sp;
    pos++;p[u]=pos;
    if (Son[u]==-1)return ;
    dfs2(Son[u],sp);
    for (int i=0;i<int(V[u].size());i++){
        int v=V[u][i];
        if (Fa[u]!=v && Son[u]!=v)dfs2(v,v);
    }
}
void Push_down(int p,int L,int R)
{
    if (T[p].m==1){
        T[p+p].cl=T[p+p+1].cl=T[p].cl;
        T[p+p].cr=T[p+p+1].cr=T[p].cr;
        T[p+p].m=T[p+p+1].m=T[p].m;
    }
}
void Push_up(int p,int L,int R)
{
    T[p].cl=T[p+p].cl;T[p].cr=T[p+p+1].cr;
    T[p].m=T[p+p].m+T[p+p+1].m;
    if (T[p+p].cr==T[p+p+1].cl)T[p].m--;
}
void Build(int p,int L,int R)
{
    if (L==R){
            T[p].cl=vp[L];
            T[p].cr=vp[L];
            T[p].m=1;
            return ;
    }
    int mid=(L+R)>>1;
    Build(p+p,L,mid);
    Build(p+p+1,mid+1,R);
    Push_up(p,L,R);
}
int Find(int p,int L,int R,int l,int r,int gdl,int gdr)
{
    if (L==gdl)cL=T[p].cl;
    if (R==gdr)cR=T[p].cr;
    if (L==l && R==r)return T[p].m;
    Push_down(p,L,R);
    int mid=(L+R)>>1;
    if (r<=mid)return Find(p+p,L,mid,l,r,gdl,gdr);else
        if (mid< l)return Find(p+p+1,mid+1,R,l,r,gdl,gdr);else{
            int t1=Find(p+p,L,mid,l,mid,gdl,gdr)+Find(p+p+1,mid+1,R,mid+1,r,gdl,gdr);
            if (T[p+p].cr==T[p+p+1].cl)t1--;
            return t1;
        }
    Push_up(p,L,R);
}
int Query(int u,int v)
{
    int Res=0;
    int f1=Top[u],f2=Top[v];
    int pre1=-1,pre2=-1;
    while (f1!=f2){
        if (d[f1]<d[f2]){
            swap(pre1,pre2);swap(f1,f2);swap(u,v);
        }
        Res+=Find(1,1,pos,p[f1],p[u],p[f1],p[u]);//x相同的边
        if (pre1==cR)Res--;
        pre1=cL;u=Fa[f1];f1=Top[u];
    }

    if (u==v){
            if (pre1!=-1 && pre1==pre2)Res--;
            return Res;
    }
    if (d[u]<d[v])swap(u,v),swap(pre1,pre2);
    Res+=Find(1,1,pos,p[Son[v]],p[u],p[Son[v]],p[u]);//相同的边
    if (pre1!=-1 && pre1==cR)Res--;
    if (pre2!=-1 && pre2==cL)Res--;
    return Res;
}
void Insert(int p,int L,int R,int l,int r,int v)
{
    if (L==l && R==r){
        T[p].cl=T[p].cr=v;T[p].m=1;
        return ;
    }
    Push_down(p,L,R);
    int mid=(L+R)>>1;
    if (r<=mid)Insert(p+p,L,mid,l,r,v);else
    if (mid< l)Insert(p+p+1,mid+1,R,l,r,v);else{
        Insert(p+p,L,mid,l,mid,v);
        Insert(p+p+1,mid+1,R,mid+1,r,v);
    }
    Push_up(p,L,R);
}
void Change(int u,int v,int w)
{
    int f1=Top[u],f2=Top[v];
    while (f1!=f2){
        if (d[f1]<d[f2]){
            swap(f1,f2);swap(u,v);
        }
        Insert(1,1,pos,p[f1],p[u],w);
        u=Fa[f1];f1=Top[u];
    }

    if (u==v)return ;
    if (d[u]<d[v])swap(u,v);
    Insert(1,1,pos,p[Son[v]],p[u],w);
}
int main()
{
    //freopen("1.txt","r",stdin);
    while (scanf("%d%d",&n,&m)!=EOF){
        for (int i=1;i<=n;i++)V[i].clear();
        for (int i=1;i<n;i++){
            int u,v,w;scanf("%d%d%d",&u,&v,&w);
            Edge[i][0]=u;
            Edge[i][1]=v;
            Edge[i][2]=w;
            V[u].push_back(v);
            W[u].push_back(w);
            V[v].push_back(u);
            W[v].push_back(w);
        }
        for (int i=1;i<=n;i++)Son[i]=-1;
        pos=0;
        dfs1(1,0,0);
        dfs2(1,1);
        //for (int i=1;i<=n;i++)printf("%d:%d\n",i,p[i]);
        for (int i=1;i<n;i++){
            if (d[Edge[i][0]]<d[Edge[i][1]])
                swap(Edge[i][0],Edge[i][1]);
            vp[p[Edge[i][0]]]=Edge[i][2];
            //Insert(1,1,pos,p[Edge[i][0]],p[Edge[i][0]],Edge[i][2]);
        }
        Build(1,1,pos);

        /*for (int i=1;i<(n<<2);i++){
            printf("%d:%d %d %d\n",i,T[i].cl,T[i].cr,T[i].m);
        }*/
        char str[10];
        while (m--){
            scanf("%s",str);
            if (str[0]=='Q'){
                int x,y;scanf("%d%d",&x,&y);
                printf("%d\n",Query(x,y));
            }else{
                int x,y,z;scanf("%d%d%d",&x,&y,&z);
                Change(x,y,z);
            }
        }
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值