zoukankan      html  css  js  c++  java
  • [SPOJ375]Qtree

    题目

    Description

    Query on a tree Problem code: QTREE

    You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1.

    We will ask you to perfrom some instructions of the following form:

    CHANGE i ti : change the cost of the i-th edge to ti or QUERY a b : ask for the maximum edge cost on the path from node a to node b Input The first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow.

    给一棵共有 n(n · 10000) 个结点的树, 每条边都有一个权值, 要求维护一个数据结构, 支持如下操作:

    1. 修改某条边的权值;
    2. 询问某两个结点之间的唯一通路上的最大边权. 其中操作的总次数为 q.

    Input

    For each test case:

    In the first line there is an integer N (N <= 10000), In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of cost c (c <= 1000000), The next lines contain instructions "CHANGE i ti" or "QUERY a b", The end of each test case is signified by the string "DONE". There is one blank line between successive tests.

    对提问的读入一直到DONE为止。 一开始有个t,表示t组数据。

    Output

    For each "QUERY" operation, write one integer representing its result.

    Sample Input

    1
    3
    1 2 1
    2 3 2
    QUERY 1 2
    CHANGE 1 3
    QUERY 1 2
    DONE
    

    Sample Output

    1
    3

    思路

    这是一道比较裸的树链剖分的题;

    如果还有点不懂的读者可以看看

    树链剖分入门(浅谈树链剖分)

    相比,模板题

    [ZJOI2008] 树的统计Count 

    不同的是要修改边的权值;

    那么我们将每个$x$ 到$xx$ 边的权值,统计到 $xx$ 上;

    那么访问区间 $x$---$y$ 的最大值时 $x$ 就要减去一个 $1$;

    那么我们可以用 $l[i]$,   $r[i]$表示每条边的两个端点;

    每次$change$ 修改边权的时候就:

    判断$dep[l[i]]$ 和 $dep[r[i]]$ 的深度大小;

    由于我们是将边的权值附给 $xx$ 子节点上;

    所以 修改要在 $dep$ 深度大的节点上;

        if(dep[l[x]]>dep[r[x]])//l[x] 深度大说明 l[x] 是子节点
            change(1,id[l[x]],y);//单点修改
            else//否则 r[x] 是子节点
            change(1,id[r[x]],y);//单点修改

    其他的都差不多,具体看代码吧

    代码

    #pragma GCC optimize(3)
    #pragma GCC target("avx")
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("inline")
    #pragma GCC optimize("-fgcse")
    #pragma GCC optimize("-fgcse-lm")
    #pragma GCC optimize("-fipa-sra")
    #pragma GCC optimize("-ftree-pre")
    #pragma GCC optimize("-ftree-vrp")
    #pragma GCC optimize("-fpeephole2")
    #pragma GCC optimize("-ffast-math")
    #pragma GCC optimize("-fsched-spec")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC optimize("-falign-jumps")
    #pragma GCC optimize("-falign-loops")
    #pragma GCC optimize("-falign-labels")
    #pragma GCC optimize("-fdevirtualize")
    #pragma GCC optimize("-fcaller-saves")
    #pragma GCC optimize("-fcrossjumping")
    #pragma GCC optimize("-fthread-jumps")
    #pragma GCC optimize("-funroll-loops")
    #pragma GCC optimize("-fwhole-program")
    #pragma GCC optimize("-freorder-blocks")
    #pragma GCC optimize("-fschedule-insns")
    #pragma GCC optimize("inline-functions")
    #pragma GCC optimize("-ftree-tail-merge")
    #pragma GCC optimize("-fschedule-insns2")
    #pragma GCC optimize("-fstrict-aliasing")
    #pragma GCC optimize("-fstrict-overflow")
    #pragma GCC optimize("-falign-functions")
    #pragma GCC optimize("-fcse-skip-blocks")
    #pragma GCC optimize("-fcse-follow-jumps")
    #pragma GCC optimize("-fsched-interblock")
    #pragma GCC optimize("-fpartial-inlining")
    #pragma GCC optimize("no-stack-protector")
    #pragma GCC optimize("-freorder-functions")
    #pragma GCC optimize("-findirect-inlining")
    #pragma GCC optimize("-fhoist-adjacent-loads")
    #pragma GCC optimize("-frerun-cse-after-loop")
    #pragma GCC optimize("inline-small-functions")
    #pragma GCC optimize("-finline-small-functions")
    #pragma GCC optimize("-ftree-switch-conversion")
    #pragma GCC optimize("-foptimize-sibling-calls")
    #pragma GCC optimize("-fexpensive-optimizations")
    #pragma GCC optimize("-funsafe-loop-optimizations")
    #pragma GCC optimize("inline-functions-called-once")
    #pragma GCC optimize("-fdelete-null-pointer-checks")
    #pragma GCC optimize(2)//为了让冗长的代码看起来更长 
    #include<bits/stdc++.h>//头文件 
    #define re register//宏定义 
    typedef long long ll;
    using namespace std;
    inline ll read()//快读 
    {
        ll a=0,f=1; char c=getchar();//a 是数字大小,f 是判正负
        //???为什么快读也要写注释 
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    ll n,m;
    ll l[200010],r[200010];
    ll w[200010];//w 记录权值 
    ll head[200010];
    ll size[200010],dep[200010],top[200010];
    //size 记录子树 节点个数  ,dep 记录深度, top 记录这条链的顶部 
    ll id[200010],aa[200010];//id 是在线段树中的编号,aa 是在线段树中的权值 
    ll f[200010],son[200010];// f 是父节点,son 是重子节点 
    struct ljj
    {
        ll to,stb,w;
    }e[200010];//to 表示这条边到达的点,stb 表示上一条边  w表示权值 
    struct ljq
    {
        ll l,r,mx;
    }a[200010];//线段树基本变量 
    inline ll L(ll x)
    {
        return 2*x;
    }//线段树中左儿子的编号 
    inline ll R(ll x)
    {
        return 2*x+1;
    }//线段树中右儿子的编号 
    ll s=0;
    inline void insert(ll x,ll y,ll z)
    {
        s++;
        e[s].stb=head[x];
        e[s].w=z;
        e[s].to=y;
        head[x]=s;
    }
    inline void dfs(ll x,ll fa)//找重子节点 
    {
        size[x]=1;
        f[x]=fa;//记录父节点 
        for(re ll i=head[x];i;i=e[i].stb)
        {
            ll xx=e[i].to;
            if(xx==fa)//不能遍历到父节点 
                continue;
            w[xx]=e[i].w;
            dep[xx]=dep[x]+1;//统计深度 
            dfs(xx,x);
            size[x]+=size[xx];//统计子树节点数 
            if(!son[x]||size[xx]>size[son[x]])
                son[x]=xx;//找重子节点,也就是子树节点数最多的子节点 
        }
    }
    ll tot=0;//统计在线段树中的编号 
    inline void DFS(ll x,ll t)//t 表示这条链的顶部 
    {
        top[x]=t;//记录 
        id[x]=++tot;//记录在线段树中的编号 
        aa[tot]=w[x];//记录在线段树中的权值 
        if(!son[x])//如果没有重子节点 
            return;//返回 
        DFS(son[x],t);//先遍历重子节点 
        for(re ll i=head[x];i;i=e[i].stb)
        {
            ll xx=e[i].to;
            if(xx==f[x]||xx==son[x])//遍历轻子节点 
                continue;
            DFS(xx,xx);//每个开始的轻子节点的链顶就是自己 
        }
    }
    inline void doit(ll p)//维护区间 
    {
        a[p].mx=max(a[L(p)].mx,a[R(p)].mx);
    }
    inline void build(ll p,ll l,ll r)//建树 
    {
        a[p].l=l; a[p].r=r;
        if(l==r)
        {
            a[p].mx=aa[l];
            return;
        }
        ll mid=(l+r)>>1;
        build(L(p),l,mid);
        build(R(p),mid+1,r);
        doit(p);
    }
    inline void change(ll p,ll x,ll y)//单点修改 
    {
        if(a[p].l==a[p].r)
        {
            a[p].mx=y;
            return;
        }
        ll mid=(a[p].l+a[p].r)>>1;
        if(x<=mid)
            change(L(p),x,y);
        else
            change(R(p),x,y);
        doit(p);
    }
    inline ll findout(ll p,ll l,ll r)//找区间最大值 
    {
        if(l<=a[p].l&&a[p].r<=r)
            return a[p].mx;
        ll sum=-(1<<30);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            sum=max(sum,findout(L(p),l,r));
        if(r>mid)
            sum=max(sum,findout(R(p),l,r));
        return sum;
    }
    inline ll qmax(ll x,ll xx)
    {
        ll sum=-(1<<30);
        while(top[x]!=top[xx])//我们需要是 x 节点跳到与 xx 节点在同一条链上 
        {
            if(dep[top[x]]<dep[top[xx]])//深度大的往上跳 
                swap(x,xx);
            sum=max(sum,findout(1,id[top[x]],id[x]));//统计 x 到链顶的 最大值 
            x=f[top[x]];// 跳到下一个区间,也就是在 top[x] 上面的链 
        }
        if(dep[x]<dep[xx])
            swap(x,xx);
        sum=max(sum,findout(1,id[xx]+1,id[x]));//在统计下 x 到 xx 的区间最大值
        //此时 x 与 xx 是在同一条链上 
        return sum;
    }
    int main()
    {
        ll o=read();//读入 
        while(o--)
        {
            memset(l,0,sizeof(l));
            memset(r,0,sizeof(r));
            memset(w,0,sizeof(w));
            memset(head,0,sizeof(head));
            memset(size,0,sizeof(size));
            memset(dep,0,sizeof(dep));
            memset(top,0,sizeof(top));
            memset(id,0,sizeof(id));
            memset(aa,0,sizeof(aa));
            memset(f,0,sizeof(f));
            memset(son,0,sizeof(son));
            memset(a,0,sizeof(a));
            memset(e,0,sizeof(e));
            tot=0; s=0;//全部重置!!! 
            n=read();
            for(re ll i=1;i<n;i++)
            {
                ll x=read(),y=read(),z=read();//读入 
                l[i]=x; r[i]=y;//记下线段的左右端点 
                insert(x,y,z);
                insert(y,x,z);//连边 
            }
            dfs(1,0);
            DFS(1,1);
            build(1,1,n);// 
            while(1)
            {
                char c[5];
                scanf("%s",c);
                if(c[0]=='D')
                    break;
                if(c[0]=='C')
                {
                    ll x=read(),y=read();
                    if(dep[l[x]]>dep[r[x]])//l[x] 深度大说明 l[x] 是子节点
                        change(1,id[l[x]],y);//单点修改
                    else//否则 r[x] 是子节点
                        change(1,id[r[x]],y);//单点修改
                }
                else
                {
                    ll x=read(),y=read();
                    ll ans=qmax(x,y);//找区间最大值 
                    printf("%d
    ",ans);//输出 
                }
            }
        }
        return 0;// 竟然打了return 0!!! 
    }
  • 相关阅读:
    JDBC连接数据库
    Promise的基本用法
    Cookie、LocalStorage 与 SessionStorage的区别在哪里?
    判断时间大小,数值大小
    控制input文本框只能输入正整数
    利用layer弹框代替alert效果,且可以增加callback函数
    input 只能输入数字且控制位数
    递归函数
    分页插件,直接把返回值赋过去即可
    获取选中checkbox的value值
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13520591.html
Copyright © 2011-2022 走看看