P3313 [SDOI2014] 旅行

洛谷传送门

题意:给出一个树,每个树的节点有俩个值,一个表示属于某个集合,另一个表示改节点的权值,有q个询问,每次询问可能有4个类型,修改节点的集合属性和权值属性,输出某个节点到另一个节点的属于同一个集合的权值之和或者权值最大值。

思路:重链剖分+动态线段树。

一:树剖(即重链剖分)

可以说是树剖的经典运用,将树剖分成一个链,此题主要难在如何处理线段树上,代码如下:

int sum[N],dep[N],fa[N],son[N],top[N];
int pre[N],new_point[N];
int tot;
int n,q;
int tot_2;
vector<int>f[N];
void dfs_1(int father, int cur) {
    sum[cur] = 1;
    for (auto v: f[cur]) {
        if (v == father) continue;
        fa[v] = cur;
        dep[v] = dep[cur] + 1;
        dfs_1(cur, v);
        sum[cur] += sum[v];
        if (sum[v] > sum[son[cur]]) son[cur] = v;
    }
}
void dfs_2(int fcur, int cur) {
    top[cur] = fcur;
    tot++;
    pre[tot] = cur;
    new_point[cur] = tot;
    if (son[cur]) dfs_2(fcur, son[cur]);
    for (auto v: f[cur]) {
        if (v == fa[cur] || v == son[cur]) continue;
        dfs_2(v, v);
    }
}

二:线段树部分

暴力思路,对于每一个集合我们都开一颗线段树,然后处理询问的时候直接在相应的线段树里找要询问的区间进行处理即可,但是这样空间会爆

优化:动态开点,不需要的子节点我们不开分支而只开需要的分支,数据拉满的话,也就只用开N*logN的空间,因为如果有N颗线段树,每颗线段树的点不会超过一个

代码如下:

void motify(int &rt,int l,int r,int val,int pos)
{
    if(!rt) rt=++tot_2;//没有build函数,有motify函数就够了
    if(l==r)
    {
        ak[rt].maxx=val;
        ak[rt].sum=val;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) motify(ak[rt].l,l,mid,val,pos);
    if(pos>mid) motify(ak[rt].r,mid+1,r,val,pos);
    ak[rt].maxx=max(ak[ak[rt].l].maxx,ak[ak[rt].r].maxx);
    ak[rt].sum=ak[ak[rt].l].sum+ak[ak[rt].r].sum;
}
akwing query(int rt,int l,int r,int l2,int r2)//用一个结构体直接存u-v路径上的答案
{
    if(l>=l2&&r<=r2) return ak[rt];
    int mid=l+r>>1;
    akwing res;
    if(r2<=mid) res=query(ak[rt].l,l,mid,l2,r2);
    else if(l2>mid) res=query(ak[rt].r,mid+1,r,l2,r2);
    else
    {
        akwing l_1,r_1;
        l_1=query(ak[rt].l,l,mid,l2,r2);
        r_1=query(ak[rt].r,mid+1,r,l2,r2);
        res.maxx=max(l_1.maxx,r_1.maxx);
        res.sum=l_1.sum+r_1.sum;
    }
    return res;
}

 三:完整代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int root[N];
struct akwing
{
    int l,r,maxx,sum;
}ak[20*N];
int w[N],c[N];
int sum[N],dep[N],fa[N],son[N],top[N];
int pre[N],new_point[N];
int tot;
int n,q;
int tot_2;
vector<int>f[N];
void dfs_1(int father, int cur) {
    sum[cur] = 1;
    for (auto v: f[cur]) {
        if (v == father) continue;
        fa[v] = cur;
        dep[v] = dep[cur] + 1;
        dfs_1(cur, v);
        sum[cur] += sum[v];
        if (sum[v] > sum[son[cur]]) son[cur] = v;
    }
}
void dfs_2(int fcur, int cur) {
    top[cur] = fcur;
    tot++;
    pre[tot] = cur;
    new_point[cur] = tot;
    if (son[cur]) dfs_2(fcur, son[cur]);
    for (auto v: f[cur]) {
        if (v == fa[cur] || v == son[cur]) continue;
        dfs_2(v, v);
    }
}
void motify(int &rt,int l,int r,int val,int pos)
{
    if(!rt) rt=++tot_2;
    if(l==r)
    {
        ak[rt].maxx=val;
        ak[rt].sum=val;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) motify(ak[rt].l,l,mid,val,pos);
    if(pos>mid) motify(ak[rt].r,mid+1,r,val,pos);
    ak[rt].maxx=max(ak[ak[rt].l].maxx,ak[ak[rt].r].maxx);
    ak[rt].sum=ak[ak[rt].l].sum+ak[ak[rt].r].sum;
}
akwing query(int rt,int l,int r,int l2,int r2)
{
    if(l>=l2&&r<=r2) return ak[rt];
    int mid=l+r>>1;
    akwing res;
    if(r2<=mid) res=query(ak[rt].l,l,mid,l2,r2);
    else if(l2>mid) res=query(ak[rt].r,mid+1,r,l2,r2);
    else
    {
        akwing l_1,r_1;
        l_1=query(ak[rt].l,l,mid,l2,r2);
        r_1=query(ak[rt].r,mid+1,r,l2,r2);
        res.maxx=max(l_1.maxx,r_1.maxx);
        res.sum=l_1.sum+r_1.sum;
    }
    return res;
}
akwing cli(int l,int r)
{
    int u=c[l];
    akwing temp,res;
    temp.sum=0,temp.maxx=-1;
    res=temp;
    while(top[l]!=top[r])
    {
        if(dep[top[l]]>dep[top[r]])
        {
            temp=query(root[u],1,n,new_point[top[l]],new_point[l]);
            res.maxx=max(res.maxx,temp.maxx);
            res.sum=res.sum+temp.sum;
            l=fa[top[l]];
        }
        else
        {
            temp=query(root[u],1,n,new_point[top[r]],new_point[r]);
            res.maxx=max(res.maxx,temp.maxx);
            res.sum=res.sum+temp.sum;
            r=fa[top[r]];
        }
    }
    if(dep[l]>dep[r]) swap(l,r);
    temp=query(root[u],1,n,new_point[l],new_point[r]);
    res.maxx=max(res.maxx,temp.maxx);
    res.sum=res.sum+temp.sum;
    return res;
}
void solve(){
    cin>>n>>q;
    for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        cin>>u>>v;
        f[u].push_back(v);
        f[v].push_back(u);
    }
    dfs_1(0,1);
    dfs_2(1,1);
//    for(int i=1;i<=n;i++) cout<<new_point[i]<<' '<<top[i]<<' '<<'\n';
//    cout<<"\n";
   for(int i=1;i<=n;i++) motify(root[c[i]],1,n,w[i],new_point[i]);//相应信仰和评级.
    for(int i=1;i<=q;i++)
    {
        string k;
        cin>>k;
        if(k=="CC")
        {
            int x,rt;
            cin>>x>>rt;//城市x的居民改信了rt教;//x城市之前信的是c[x]教,评级w[x];
            motify(root[c[x]],1,n,0,new_point[x]);
            motify(root[rt],1,n,w[x],new_point[x]);
            c[x]=rt;
        }
        else if(k=="CW")
        {
            int x,val;
            cin>>x>>val;//城市x的评级调整为val;
            motify(root[c[x]],1,n,val,new_point[x]);
            w[x]=val;
        }
        else if(k=="QS")
        {
            int x,y;
            cin>>x>>y;//从x走到y,记下了过程中的评级总和;
            cout<<cli(x,y).sum<<'\n';
        }
        else
        {
            int x,y;
            cin>>x>>y;
            cout<<cli(x,y).maxx<<"\n";
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);
    int T=1;
    while(T--){solve();}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值