P2590 [ZJOI2008]树的统计

操作:

I. CHANGE u t : 把结点u的权值改为t

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

III. QSUM u v: 询问从点u到点v的路径上的节点的权值和

标签:树剖+线段树 模板题。

转化一下题意:

操作1:单点修改。

操作2:利用树剖求LCA的过程求经过的点的权值的最大值。

操作3:利用树剖求LCA的过程累加经过的点的权值之和。

Code:

#include<cstdio>
#include<iostream>
#include<string>
#define ri register int
using namespace std;

const int MAXN=60020;
int n,m,q,u[MAXN],v[MAXN],w[MAXN],fst[MAXN],nxt[MAXN],num,t;
int fa[MAXN],deep[MAXN],siz[MAXN],son[MAXN],cmax[MAXN],top[MAXN],dfn[MAXN],rk[MAXN],cur,a[MAXN];
int l[MAXN<<2],r[MAXN<<2],maxn[MAXN<<2],sum[MAXN<<2];
string ss;

void dfs1(int x,int father,int dep)
{
    fa[x]=father,deep[x]=dep,siz[x]=1;
    for(ri k=fst[x];k>0;k=nxt[k])
        if(v[k]!=father)
        {
            dfs1(v[k],x,dep+1);
            if(siz[v[k]]>cmax[x])	cmax[x]=siz[v[k]],son[x]=v[k];
            siz[x]+=siz[v[k]];
        }
}

void dfs2(int x,int anc)
{
    top[x]=anc,dfn[x]=++cur,rk[cur]=x,a[cur]=w[x];
    if(son[x]!=0)	dfs2(son[x],anc);
    for(ri k=fst[x];k>0;k=nxt[k])
        if(v[k]!=fa[x]&&v[k]!=son[x])	dfs2(v[k],v[k]);
}

void pushup(int p)
{
    sum[p]=sum[p <<1]+sum[p <<1|1];
    maxn[p]=max(maxn[p <<1],maxn[p <<1|1]);	
}

void build(int p,int lft,int rit)
{
    l[p]=lft,r[p]=rit;
    if(lft==rit)	{ sum[p]=a[lft]; maxn[p]=a[lft]; return; }
    int mid=(lft+rit)>>1;
    build(p <<1,lft,mid);
    build(p <<1|1,mid+1,rit);
    pushup(p);
}

void update(int p,int pla,int k)
{
    if(l[p]==pla&&r[p]==pla)
    {
        sum[p]=k,maxn[p]=k;
        return; 
    }
    if(pla<=r[p <<1])    update(p <<1,pla,k);
    if(l[p <<1|1]<=pla)    update(p <<1|1,pla,k);
    pushup(p);
}

int qmax(int p,int lft,int rit)
{
    if(lft<=l[p]&&r[p]<=rit)	return maxn[p];
    int ans=-1e9;
    if(lft<=r[p <<1])    ans=qmax(p <<1,lft,rit);
    if(l[p <<1|1]<=rit)	   ans=max(ans,qmax(p <<1|1,lft,rit));
    return ans;	
}

int LCAmax(int x,int y)
{
    int ans=-1e9;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])	swap(x,y);
        ans=max(ans,qmax(1,dfn[top[x]],dfn[x]));
        x=fa[top[x]];
    }
    if(deep[x]<deep[y])	swap(x,y);
    ans=max(ans,qmax(1,dfn[y],dfn[x]));
    return ans;	
}

int qsum(int p,int lft,int rit)
{
    if(lft<=l[p]&&r[p]<=rit)	return sum[p];
    int ans=0;
    if(lft<=r[p <<1])    ans=qsum(p <<1,lft,rit);
    if(l[p <<1|1]<=rit)	   ans+=qsum(p <<1|1,lft,rit);
    return ans;
}

int LCAsum(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])	swap(x,y);
        ans+=qsum(1,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    if(deep[x]<deep[y])	swap(x,y);
    ans+=qsum(1,dfn[y],dfn[x]);
    return ans;
}

int main()
{
    scanf("%d",&n);
    m=(n-1)<<1;
    for(ri i=1;i<=m;i+=2)
    {
        scanf("%d%d",&u[i],&v[i]);
        nxt[i]=fst[u[i]],fst[u[i]]=i;
        u[i+1]=v[i],v[i+1]=u[i];
        nxt[i+1]=fst[u[i+1]],fst[u[i+1]]=i+1;
    }
    for(ri i=1;i<=n;i++)	scanf("%d",&w[i]);
    dfs1(1,1,0);
    dfs2(1,1);
    build(1,1,n);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        cin>>ss>>num>>t;
        if(ss=="CHANGE")    update(1,dfn[num],t);
        if(ss=="QMAX")    cout<<LCAmax(num,t)<<'\n';
        if(ss=="QSUM")    cout<<LCAsum(num,t)<<'\n';
    }
    return 0;
}

 

下载前可以先看下教程 https://pan.quark.cn/s/16a53f4bd595 小天才电话手表刷机教程 — 基础篇 我们将为您简单的介绍小天才电话手表新机型的简单刷机以及玩法,如adb工具的使用,magisk的刷入等等。 我们会确保您看完此教程后能够对Android系统有一个最基本的认识,以及能够成功通过magisk root您的手表,并安装您需要的第三方软件。 ADB Android Debug Bridge,简称,在android developer的adb文档中是这么描述它的: 是一种多功能命令行工具,可让您与设备进行通信。 该命令有助于各种设备操作,例如安装和调试应用程序。 提供对 Unix shell 的访问,您可以使用它在设备上运行各种命令。 它是一个客户端-服务器程序。 这听起来有些难以理解,因为您也没有必要去理解它,如果您对本文中的任何关键名词产生疑惑或兴趣,您都可以在搜索引擎中去搜索它,当然,我们会对其进行简单的解释:是一款在命令行中运行的,用于对Android设备进行调试的工具,并拥有比一般用户以及程序更高的权限,所以,我们可以使用它对Android设备进行最基本的调试操作。 而在小天才电话手表上启用它,您只需要这么做: - 打开拨号盘; - 输入; - 点按打开adb调试选项。 其次是电脑上的Android SDK Platform-Tools的安装,此工具是 Android SDK 的组件。 它包括与 Android 平台交互的工具,主要由和构成,如果您接触过Android开发,必然会使用到它,因为它包含在Android Studio等IDE中,当然,您可以独立下载,在下方选择对应的版本即可: - Download SDK Platform...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值