HDU 5692 - Snacks - [DFS序+线段树]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5692

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)

Problem Description
百度科技园内有 n个零食机,零食机之间通过n1条路相互连通。每个零食机都有一个值v,表示为小度熊提供零食的价值。

由于零食被频繁的消耗和补充,零食机的价值v会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。

为小度熊规划一个路线,使得路线上的价值总和最大。
 
Input
输入数据第一行是一个整数 T(T10),表示有T组测试数据。
对于每组数据,包含两个整数n,m(1n,m100000),表示有n个零食机,m次操作。
接下来n1行,每行两个整数xy(0x,y<n),表示编号为x的零食机与编号为y的零食机相连。
接下来一行由n个数组成,表示从编号为0到编号为n1的零食机的初始价值v(|v|<100000)
接下来m行,有两种操作:0 x y,表示编号为x的零食机的价值变为y1 x,表示询问从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。
本题可能栈溢出,辛苦同学们提交语言选择c++,并在代码的第一行加上:
`#pragma comment(linker, "/STACK:1024000000,1024000000") `
 
Output
对于每组数据,首先输出一行”Case #?:”,在问号处应填入当前数据的组数,组数从1开始计算。
对于每次询问,输出从编号为0的零食机出发,必须经过编号为 x零食机的路线中,价值总和的最大值。
 
Sample Input
1
6 5
0 1
1 2
0 3
3 4
5 3
7 -5 100 20 -5 -7
1 1
1 3
0 2 -1
1 1
1 5
 
Sample Output
Case #1:
102
27
2
20
 

题解:

题目给出n个点和n-1条边,并且是连通图,那么显然是一棵树;

一棵无向树,我们可以任意取一个节点作为树根,根据题意取编号为0的节点为树根;

那么,对于其他的1~n-1号节点,从树根(0号节点)到它们只有唯一一条路径,

假设sum[i]代表从0号节点到i号节点这条路径上所有的点(包括节点0和i)的value值之和;

(换句话说,假设从root=0,到i节点的唯一一条简单路径为0-1-3-5-i,那么 sum[i] = value[0] + value[1] + value[3] + value[5] + value[i];)

then,对于题目中描述的两种操作:

扫描二维码关注公众号,回复: 108777 查看本文章

①修改节点i的value[i]值:

  一旦修改value[i],会影响到以i为树根的子树内的所有节点的sum[],

  假设value[i]+=k,那么其正科统领的一整棵子树上的节点上的sum[]都要加上k;

②查询:从0号节点出发,经过节点x,走一条简单路径,所经过的所有节点的value值之和的最大值:

  显然,这就是枚举节点x统领的子树上所有的节点的sum[]值,找到其中最大的就行;

(注:在上面,节点x统领的子树内所有的点,包含节点x自己)

那么,如果我们使用DFS序把整棵树“拍平”,把他们排列到一串序列中……

这个序列中,节点x的in[x]和out[x]代表:[ in[x] , out[x] ]区间内的点正好是节点x统领的子树内的所有节点;

那么对于上面两种操作,我们从可以从“树上修改,树上查询”变成“区间修改,区间查询”……

即:

  ①x节点统领的子树内,所有节点的sum[]+=k → [ in[x] , out[x] ]区间内所有节点的sum[]+=k;

  ②x节点统领的子树内,查询所有节点中sum[]的最大值 → 查询[ in[x] , out[x] ]区间内所有节点的sum[]最大值;

显然,如果使用线段树进行维护,两种操作就都从O(n)时间复杂度变成了O(lgn)的时间复杂度;

AC代码:

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

const int maxn=100000+5;
const LL INF=1e18;

int n,m;
LL val[maxn],sum[maxn];

//邻接表-st
struct Edge{
    int u,v;
    Edge(int u,int v){this->u=u,this->v=v;}
};
vector<Edge> E;
vector<int> G[maxn];
void Adjacency_List_Init(int l,int r)
{
    E.clear();
    for(int i=l;i<=r;i++) G[i].clear();
}
void addedge(int u,int v)
{
    E.push_back(Edge(u,v));
    E.push_back(Edge(v,u));
    int _size=E.size();
    G[u].push_back(_size-2);
    G[v].push_back(_size-1);
}
//邻接表-ed

//dfs序-st
int dfs_clock;
bool vis[maxn];
int in[maxn],out[maxn];
int peg[maxn];
inline void DFS_Init()
{
    dfs_clock=0;
    memset(vis,0,sizeof(vis));
}
void dfs(int now)
{
    in[now]=++dfs_clock;
    peg[in[now]]=now;

    vis[now]=1;
    for(int i=0,_size=G[now].size();i<_size;i++)
    {
        int nxt=E[G[now][i]].v;
        if(!vis[nxt])
        {
            sum[nxt]=sum[now]+val[nxt];
            dfs(nxt);
        }
    }

    out[now]=dfs_clock;
}
//dfs序-ed

//线段树-st
struct Node{
    int l,r;
    LL val,lazy;
    void update(LL x)
    {
        val+=x;
        lazy+=x;
    }
}node[4*maxn];
void pushdown(int root)
{
    if(node[root].lazy)
    {
        node[root*2].update(node[root].lazy);
        node[root*2+1].update(node[root].lazy);
        node[root].lazy=0;
    }
}
void pushup(int root)
{
    node[root].val=max(node[root*2].val,node[root*2+1].val);
}
void build(int root,int l,int r)
{
    node[root].l=l; node[root].r=r;
    node[root].val=0; node[root].lazy=0;
    if(l==r) node[root].val=sum[peg[l]];
    else
    {
        int mid=l+(r-l)/2;
        build(root*2,l,mid);
        build(root*2+1,mid+1,r);
        pushup(root);
    }
}
void update(int root,int st,int ed,int val)
{
    if(st>node[root].r || ed<node[root].l) return;
    if(st<=node[root].l && node[root].r<=ed) node[root].update(val);
    else
    {
        pushdown(root);
        update(root*2,st,ed,val);
        update(root*2+1,st,ed,val);
        pushup(root);
    }
}
LL query(int root,int st,int ed)
{
    if(ed<node[root].l || node[root].r<st) return -INF;
    if(st<=node[root].l && node[root].r<=ed) return node[root].val;
    else
    {
        pushdown(root);
        LL lson=query(root*2,st,ed);
        LL rson=query(root*2+1,st,ed);
        pushup(root);
        return max(lson,rson);
    }
}
//线段树-ed


int main()
{
    int t;
    scanf("%d",&t);
    for(int kase=1;kase<=t;kase++)
    {
        scanf("%d%d",&n,&m);

        Adjacency_List_Init(0,n-1);
        for(int i=1,u,v;i<=n-1;i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
        }

        for(int i=0;i<n;i++) scanf("%I64d",&val[i]);

        DFS_Init();
        sum[0]=val[0];
        dfs(0);

        build(1,1,n);
        printf("Case #%d:\n",kase);
        for(int i=1,type,x,y;i<=m;i++)
        {
            scanf("%d",&type);
            if(type==0)
            {
                scanf("%d%d",&x,&y);
                update(1,in[x],out[x],y-val[x]);
                val[x]=y;
            }
            if(type==1)
            {
                scanf("%d",&x);
                printf("%I64d\n",query(1,in[x],out[x]));
            }
        }
    }
}

注意:这里的线段树,是区间更新(一个区间加上一个值),区间查询(一个区间的val维护:区间内所有节点最大值)。

Problem Description
百度科技园内有 n个零食机,零食机之间通过 n1条路相互连通。每个零食机都有一个值 v,表示为小度熊提供零食的价值。

由于零食被频繁的消耗和补充,零食机的价值 v会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。

为小度熊规划一个路线,使得路线上的价值总和最大。
 
Input
输入数据第一行是一个整数 T(T10),表示有 T组测试数据。

对于每组数据,包含两个整数 n,m(1n,m100000),表示有 n个零食机, m次操作。

接下来 n1行,每行两个整数 xy(0x,y<n),表示编号为 x的零食机与编号为 y的零食机相连。

接下来一行由 n个数组成,表示从编号为0到编号为 n1的零食机的初始价值 v(|v|<100000)

接下来 m行,有两种操作: 0 x y,表示编号为 x的零食机的价值变为 y1 x,表示询问从编号为0的零食机出发,必须经过编号为 x零食机的路线中,价值总和的最大值。

本题可能栈溢出,辛苦同学们提交语言选择c++,并在代码的第一行加上:

`#pragma comment(linker, "/STACK:1024000000,1024000000") `
 
Output
对于每组数据,首先输出一行”Case #?:”,在问号处应填入当前数据的组数,组数从1开始计算。

对于每次询问,输出从编号为0的零食机出发,必须经过编号为 x零食机的路线中,价值总和的最大值。
 
Sample Input
1 6 5 0 1 1 2 0 3 3 4 5 3 7 -5 100 20 -5 -7 1 1 1 3 0 2 -1 1 1 1 5
 
Sample Output
Case #1: 102 27 2 20

猜你喜欢

转载自www.cnblogs.com/dilthey/p/8988368.html