洛谷P3313&BZOJ-3531【SDOI2014】旅行--线段树动态开点+树链剖分

题目链接:https://www.luogu.com.cn/problem/P3313

时间限制  1.00s
内存限制  125.00MB
 

题目描述

S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。

为了方便,我们用不同的正整数代表各种宗教, S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。

在S国的历史上常会发生以下几种事件:

“CC x c“:城市x的居民全体改信了c教;

“CW x w“:城市x的评级调整为w;

“QS x y“:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和;

“QM x y“:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级最大值。

由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

输入格式

输入的第一行包含整数N,Q依次表示城市数和事件数。

接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的评级和信仰。 接下来N-1行每行两个整数x,y表示一条双向道路。

接下来Q行,每行一个操作,格式如上所述。

输出格式

对每个QS和QM事件,输出一行,表示旅行者记下的数字。

输入输出样例

输入 
5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4
输出
8
9
11
3

说明/提示

N,Q < =10^5 , C < =10^5

数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时

刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。


一眼树剖,然后就不会了。。。难点在于只计算经过的相同信仰的城市,这个处理好了就是个裸的树剖了。

至于怎么处理,我们可以对每种信仰建一棵树,但由于信仰的范围很大,所以为了避免MLE,我们必须动态开点,动态分配所需要的内存。

于是乎,这题好像也就结束了。。。

对每个信仰建树:

for (int i=1; i<=n; i++)
     update(1,n,a[i].w,id[i],root[a[i].c]);

即信仰为a[i].c的树根编号存储在root中,到时候树剖查询的时候我们只需要从信仰为c的树,其根为root[c]的树中开始找就可以了。

至于update函数,就是普通的动态开点过程:

inline void pushup(int rt)
{
    tree[rt].sum=tree[tree[rt].l].sum+tree[tree[rt].r].sum;
    tree[rt].maxx=max(tree[tree[rt].l].maxx,tree[tree[rt].r].maxx);
}

inline void update(int l,int r,int val,int pos,int &rt)
{
    if (!rt) rt=++pot;//动态开点
    if (l==r){
        tree[rt].sum=tree[rt].maxx=val;
        return;
    }
    int mid=(l+r)>>1;
    if (mid>=pos) update(l,mid,val,pos,tree[rt].l);
    else update(mid+1,r,val,pos,tree[rt].r);
    pushup(rt);
}

然后走一波树剖准备工作,重儿子,节点深度,top数组之类的准备完毕,接下来就是跑操作了,更改级别是没什么难度,就是直接调用update函数就OK了:

update(1,n,val,id[x],root[a[x].c]);

更改信仰也是一样的操作,不过它需要update两次,第一次将原来的信仰树中的值删除,即赋值为0,第二次在现在的信仰树中更新:

update(1,n,0,id[pos],root[a[pos].c]);
a[pos]=node{w,c};
update(1,n,a[pos].w,id[pos],root[a[pos].c]);

更新操作就这样了,不是很难。

接下来就是跑树剖了,照着板子敲一遍就完事了:

inline int query_range(int l,int r,int flag)
{
    int lv=a[l].c,s=0;
    while (top[l]!=top[r]){
        if (deep[top[l]]<deep[top[r]]) swap(l,r);
        if (!flag) s+=query(1,n,id[top[l]],id[l],root[lv],0);
        else s=max(s,query(1,n,id[top[l]],id[l],root[lv],1));
        l=fa[top[l]];
    }
    if (deep[l]>deep[r]) swap(l,r);
    if (!flag) s+=query(1,n,id[l],id[r],root[lv],0);
    else s=max(s,query(1,n,id[l],id[r],root[lv],1));
    return s;
}

至于query函数的话。。。学了线段树的都会写吧,就不贴了。

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

const int mac=1e5+10;

struct node
{
    int w,c;
}a[mac];
struct Edge
{
    int to,next;
}eg[mac<<1];
struct Tree
{
    int l,r,sum,maxx;
}tree[mac*30];
int num=0,head[mac],deep[mac],top[mac],id[mac],n;
int son[mac],dson[mac],fa[mac],use=0,root[mac],pot=0;

inline void in(int &x)
{
    char ch=getchar();
    int f=0;
    while (ch>'9' || ch<'0') ch=getchar();
    while (ch<='9' && ch>='0') f=(f<<1)+(f<<3)+ch-'0',ch=getchar();
    x=f;
}

inline void add(int u,int v)
{
    eg[++num]=Edge{v,head[u]};
    head[u]=num;
}

inline void dfs1(int x,int f)
{
    son[x]=1;fa[x]=f;
    int maxson=-1;
    for (int i=head[x]; i!=-1; i=eg[i].next){
        int v=eg[i].to;
        if (v==f) continue;
        deep[v]=deep[x]+1;
        dfs1(v,x);
        son[x]+=son[v];
        if (son[v]>maxson) dson[x]=v,maxson=son[v];
    }
}

inline void dfs2(int x,int tp)
{
    id[x]=++use;top[x]=tp;
    if (!dson[x]) return;
    dfs2(dson[x],tp);
    for (int i=head[x]; i!=-1; i=eg[i].next){
        int v=eg[i].to;
        if (v==fa[x] || v==dson[x]) continue;
        dfs2(v,v);
    }
}

inline void pushup(int rt)
{
    tree[rt].sum=tree[tree[rt].l].sum+tree[tree[rt].r].sum;
    tree[rt].maxx=max(tree[tree[rt].l].maxx,tree[tree[rt].r].maxx);
}

inline void update(int l,int r,int val,int pos,int &rt)
{
    if (!rt) rt=++pot;
    if (l==r){
        tree[rt].sum=tree[rt].maxx=val;
        return;
    }
    int mid=(l+r)>>1;
    if (mid>=pos) update(l,mid,val,pos,tree[rt].l);
    else update(mid+1,r,val,pos,tree[rt].r);
    pushup(rt);
}

inline void update_city(int pos,int c,int w,int flag)
{
    if (flag) update(1,n,0,id[pos],root[a[pos].c]);
    a[pos]=node{w,c};
    update(1,n,a[pos].w,id[pos],root[a[pos].c]);
}

inline int query(int l,int r,int L,int R,int rt,int flag)
{
    int ans=0;
    if (l>=L && r<=R){
        if (!flag) return tree[rt].sum;
        else return tree[rt].maxx;
    }
    int mid=(l+r)>>1;
    if (mid>=L) {
        if (!flag) ans+=query(l,mid,L,R,tree[rt].l,flag);
        else ans=max(ans,query(l,mid,L,R,tree[rt].l,flag));
    }
    if (mid<R){
        if (!flag) ans+=query(mid+1,r,L,R,tree[rt].r,flag);
        else ans=max(ans,query(mid+1,r,L,R,tree[rt].r,flag));
    }
    return ans;
}

inline int query_range(int l,int r,int flag)
{
    int lv=a[l].c,s=0;
    while (top[l]!=top[r]){
        if (deep[top[l]]<deep[top[r]]) swap(l,r);
        if (!flag) s+=query(1,n,id[top[l]],id[l],root[lv],0);
        else s=max(s,query(1,n,id[top[l]],id[l],root[lv],1));
        l=fa[top[l]];
    }
    if (deep[l]>deep[r]) swap(l,r);
    if (!flag) s+=query(1,n,id[l],id[r],root[lv],0);
    else s=max(s,query(1,n,id[l],id[r],root[lv],1));
    return s;
}

int main()
{
    //freopen("in.txt","r",stdin);
    int q;
    in(n);in(q);
    memset(head,-1,sizeof head);
    for (int i=1; i<=n; i++){
        int w,c;
        in(w);in(c);
        a[i]=node{w,c};
    }
    for (int i=1; i<n; i++){
        int u,v;
        in(u);in(v);
        add(u,v);add(v,u);
    }
    deep[1]=id[1]=top[1]=1;
    dfs1(1,0);
    dfs2(1,1);
    for (int i=1; i<=n; i++)
        update(1,n,a[i].w,id[i],root[a[i].c]);
    while (q--){
        char s[5];
        int l,r,x,w,c;
        scanf ("%s",s);
        if (s[1]=='C'){
            in(x);in(c);
            update_city(x,c,a[x].w,1);
        }
        else if (s[1]=='W'){
            in(x);in(w);
            update_city(x,a[x].c,w,0);
        }
        else if (s[1]=='S'){
            in(l);in(r);
            int ans=query_range(l,r,0);
            printf ("%d\n",ans);
        }
        else{
            in(l);in(r);
            int ans=query_range(l,r,1);
            printf ("%d\n",ans);
        }
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/lonely-wind-/p/11999836.html