zoukankan      html  css  js  c++  java
  • Snacks HDU 5692 dfs序列+线段树

    Snacks HDU 5692 dfs序列+线段树

    题意

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

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

    为小度熊规划一个路线,使得路线上的价值总和最大

    输入输出:

    输入数据第一行是一个整数T(T≤10),表示有T组测试数据。

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

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

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

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

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

    #pragma comment(linker, "/STACK:1024000000,1024000000") 
    

    题解思路

    dfs序列的题,这是第一道,当然不出意外,我不会做。但是我找到一篇写的非常好的博客,详细解释了这个题。点我链接

    思路就是用dfs序列,将树“拍”成一维的一串,这样就可以用线段树或者树状数组进行接下来的操作了。

    至于为什么要转化为一维的,因为简单啊,并且用来处理一维数据的方法也很多。

    代码实现

    两种形似,第一个是数组形式的线段树,有注释。第二种是使用结构体线段树,和第一种基本一样。

    #pragma comment(linker, "/STACK:1024000000,1024000000")
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <iostream>
    
    using namespace std;
    
    vector <int> a[100001];
    int n,m,now = 0,b[100001],xulie[100005],e[100005];
    long long v[100005],dis[100005] ={0},maxsum[100005*4],lazy_tag[100005*4] = {0};
    
    void dfs(int x,int pre)//深搜找dfs序
    {
        b[x] = ++now;//这是这个节点的开始位置
        xulie[now] = x;//把这个序号的节点加入到序列中
        int len = a[x].size();//这是x节点的出度
        for(int i = 0;i <= len-1;i++)//vector是从0开始计数的
        {
            int y = a[x][i];//x的一个出度。
            if (y!=pre)//如果没有往回走
            {
                dis[y] = dis[x]+v[y];//更新y节点它到0点的价值和
                dfs(y,x);//继续dfs
            }
        }
        e[x] = now;//这是节点的结束位置。b[x]..e[x]里面存的都是x的子树节点。
    }
    
    long long max(long long a,long long b)
    {
        return a > b?a:b;
    }
    
    void push_up(int rt)
    {
        maxsum[rt] = max(maxsum[rt<<1],maxsum[rt<<1|1]);//只要左右区间的最值取max就可以了。
    }
    
    void build(int l,int r,int rt)
    {
        lazy_tag[rt] = maxsum[rt] = 0;//初始化懒惰标记和区间最值。
        if (l == r)//如果左右指针相同
        {
            maxsum[rt] = dis[xulie[l]];//这个区间的最大值就是本身
            return;//注意不要写成maxsum[rt] = dis[l],l是dfs序,xulie[l]才是这个dfs序所代表的点
        }
        int m = (l+r)>>1;//取得中点
        build(l,m,rt<<1);//左递归建树
        build(m+1,r,rt<<1|1);//右递归建树
        push_up(rt);//根据儿子更新当前区间的最值。
    }
    
    void push_down(int rt)
    {
        if (lazy_tag[rt]!=0) //如果懒惰标记标记的值不为0
        {
            maxsum[rt<<1]+=lazy_tag[rt];//更新儿子节点们的区间最值(记住是同等更新就好)
            maxsum[rt<<1|1] += lazy_tag[rt];
            lazy_tag[rt<<1]+=lazy_tag[rt];//更新儿子节点们的懒惰标记。
            lazy_tag[rt<<1|1]+=lazy_tag[rt];
            lazy_tag[rt]=0;//把当前节点的懒惰标记置为0;
        }
    }
    
    void updata(int l,int r,int num,int begin,int end,int rt) //所要修改的区间为l,r,当前节点rt的区间
    {//为begin,end,然后要递增的值为num
        if (l <= begin && end <= r) //如果当前节点所在的区间被所要修改的区间覆盖
        {
            maxsum[rt]+=num;//递增这个区间的最大值(因为是同等递增,所以直接把最大值+递增值就可以);
            lazy_tag[rt] +=num;//标一个懒惰标记。
            return;
        }
        push_down(rt);//处理懒惰标记。
        int m = (begin+end)>>1;//取中点
        if (l <= m)//往左递增值
            updata(l,r,num,begin,m,rt<<1);
        if (m < r)//往右递增值。
            updata(l,r,num,m+1,end,rt<<1|1);
        push_up(rt);//根据儿子节点更新当前节点。
    }
    
    long long query(int l,int r,int begin,int end,int rt)//要找的区间为l,r,当前节点rt所代表的区间为begin,end;
    {
        if (l <= begin && end <= r)//如果当前节点所在的区间完全在要找的区间内
            return maxsum[rt];//就直接返回这个区间的最大值
        long long temp_ans= -10000000000;//这是用于取左右区间最大值的较大值。
        int m = (begin+end)>>1;
        push_down(rt);//先处理懒惰标记!!!!!!!!!!!!
        if (l <= m)//如果左区间和所找的区间有交集,就看看左区间是否更大
            temp_ans = max(temp_ans,query(l,r,begin,m,rt<<1));
        if (m < r)//右区间同理
            temp_ans = max(temp_ans,query(l,r,m+1,end,rt<<1|1));
        return temp_ans;//返回左右区间的较大值即可。
    }
    
    int main()
    {
        //freopen("rush.txt","r",stdin);
        int t;
        scanf("%d",&t);//输入数据的组数
        for (int ii = 1;ii <= t;ii++)//不要写成while (t--)。。。这样写你要怎么输出呢。。。
        {
            now = 0;//dfs序号从0再重新开始
            for (int i = 0;i <= 100000;i++)//初始化某个点的出度信息。
                a[i].clear();
            printf("Case #%d:
    ",ii);//输出这是第几组数据
            scanf("%d%d",&n,&m);//输入点和边的信息。
            for (int i = 1;i <= n-1;i++)//输入n-1条边(就是树了!)
            {
                int x,y;
                scanf("%d%d",&x,&y);//输入起点和终点
                a[x].push_back(y);//建一条双向边。
                a[y].push_back(x);
            }
            for (int i = 0;i <= n-1;i++)//输入点的初始价值。
                scanf("%lld",&v[i]);
            dis[0] = v[0];//从0到0就是0点的价值。
            dfs(0,-1);//进行dfs,获取0到所有点的价值和。
            build(1,n,1);//建树。
            for (int i = 1;i <= m;i++)//输入m个操作。
            {
                int op,x,y;
                scanf("%d",&op);//读取操作
                if (op == 0)//如果要改变某个值
                {
                    scanf("%d%d",&x,&y);
                    updata(b[x],e[x],y-v[x],1,n,1);//把替换操作改为递增操作。要进行修改的区间就是
                    v[x] = y;//x的子树所在的区间。
                    //v[x]下次可能还会用到所以要修改一下。
                }
                else
                {
                    scanf("%d",&x);//读入x,表示要经过x(然后从x和x的子树节点里面选一个节点到那里)
                    cout << query(b[x],e[x],1,n,1) << endl;//在x的子树节点所在区间内找最大价值
                }
            }
        }
        return 0;
    }
    
    #pragma comment(linker, "/STACK:1024000000,1024000000") 
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+7;
    int in[maxn], out[maxn], xulie[maxn];
    ll dis[maxn], v[maxn];
    vector<int> g[maxn];
    struct node{
    	int l, r;
    	ll maxx, lazy;
    }t[maxn<<2];
    int n, m, now;
    void init()
    {
    	now=0;
    	for(int i=0; i<=n; i++)
    	{
    		g[i].clear();
    	}
    }
    void dfs(int rt, int fa)
    {
    	in[rt]=++now;
    	xulie[now]=rt;
    	int len=g[rt].size();
    	for(int i=0; i<len; i++)
    	{
    		int y=g[rt][i];
    		if(y==fa) continue;
    		dis[y]=dis[rt]+v[y];
    		dfs(y, rt);
    	}
    	out[rt]=now;
    }
    void pushup(int rt)
    {
    	t[rt].maxx=max(t[rt<<1].maxx, t[rt<<1|1].maxx);
    }
    void build(int rt, int l, int r)
    {
    	t[rt].l=l;
    	t[rt].r=r;
    	t[rt].lazy=0;
    	if(l==r)
    	{
    		t[rt].maxx=dis[xulie[l]];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(rt<<1, l, mid);
    	build(rt<<1|1, mid+1, r);
    	pushup(rt);
    } 
    void down(int rt)
    {
    	if(t[rt].lazy!=0)
    	{
    		t[rt<<1].maxx+=t[rt].lazy;
    		t[rt<<1].lazy+=t[rt].lazy;
    		
    		t[rt<<1|1].maxx+=t[rt].lazy;
    		t[rt<<1|1].lazy+=t[rt].lazy;
    		
    		t[rt].lazy=0;
    	}
    }
    void update(int rt, int l, int r, int y)
    {
    	if(l<=t[rt].l && t[rt].r <=r)
    	{
    		t[rt].maxx+=y;
    		t[rt].lazy+=y;
    		return ;
    	}
    	down(rt);
    	int mid=(t[rt].l+t[rt].r)>>1;
    	if(l<=mid) update(rt<<1, l, r, y);
    	if(r>mid) update(rt<<1|1, l, r, y);
    	pushup(rt);
    }
    ll query(int rt, int l, int r)
    {
    	if(l<= t[rt].l && t[rt].r<=r)
    	{
    		return t[rt].maxx;
    	}
    	ll ans=-1e18;
    	int mid=(t[rt].l+t[rt].r)>>1;
    	down(rt);
    	if(l<=mid) ans=max(ans, query(rt<<1, l, r));
    	if(r>mid) ans=max(ans, query(rt<<1|1, l, r));
    	return ans;
    }
    int main()
    {
    	int t;
    	scanf("%d", &t);
    	for(int ca=1; ca<=t; ca++)
    	{
    		scanf("%d%d", &n, &m);
    		init();
    		printf("Case #%d:
    ", ca);
    		for(int i=1; i<n; i++)
    		{
    			int x, y;
    			scanf("%d%d", &x, &y);
    			g[x].push_back(y);
    			g[y].push_back(x);
    		}
    		for(int i=0; i<=n-1; i++)
    		{
    			scanf("%lld", &v[i]);
    		}
    		dis[0]=v[0];
    		dfs(0, -1);
    		build(1, 1, n);
    		for(int i=1; i<=m; i++)
    		{
    			int op, x, y;
    			scanf("%d", &op);
    			if(op==0)
    			{
    				scanf("%d%d", &x, &y);
    				update(1,in[x], out[x], y-v[x]);
    				v[x]=y;
    			}
    			else 
    			{
    				scanf("%d", &x);
    				printf("%lld
    ", query(1, in[x], out[x]));
    			}
    		}
    	}
    	return 0;
    } 
    
    欢迎评论交流!
  • 相关阅读:
    C# 文件流读写方法汇总
    C#中try catch finally的执行顺序(转载)
    Qt利用QPainter自绘实现热感应图效果
    Qt利用QPainter自绘实现扫描雷达功能scanneritem
    Qt5利用自绘QPainter实现旋转按钮MySpinButton
    Qt实现范围滑动条SuperSlider
    Qtt利用QPainter实现铵扭switchButton
    Qt5利用自绘QPainter实现水波纹进度条QProgressBarWater
    Qt5利用自绘实现云台GaugeCloud
    Qt5利用自绘实现遥感
  • 原文地址:https://www.cnblogs.com/alking1001/p/11383492.html
Copyright © 2011-2022 走看看