「BJOI2017」树的难题 TJ+卡题

「BJOI2017」树的难题 TJ+卡题

题目大意

  • 给定一棵 n 个点的树,每条边有颜色,第 i 种颜色权值为 vi,共 m 种颜色。
  • 对于树上一条路径,其权值定义为:经过边的颜色依次组成序列,每个相同颜色段的颜色权值之和。
  • 如:颜色序列 1,2,2,1,1,4,其权值为 v1+v2+v1+v4
  • 给定一对 lr,求出长度在 [l,r] 内的路径的最大权值。
  • 1n,m2×105,1l,rn,vi∣≤104

分析

这道题想清楚了很简单。

首先看到这个路径问题,就往点分治上靠,不是点分树,被骗了吧,哈哈哈

重心下单条链是很好算权值的,关键点在于两条链拼接起来,顶端的贡献问题。

想一想可以发现,其实对于每条链,其他的链顶端只有相同和不相同两种。

那就可以一种颜色一种颜色的处理,就可以很方便地维护相同和不相同的颜色。

所以第一步把儿子都提出来,然后按边的颜色排序。

将相同颜色的贡献用 now 存储,不同的用 lst 存储。

依次扫描每个儿子,进行如下操作:

  • 遍历其子树,存下每条链的长度和权值。
  • 查询 nowlst 中答案。
  • 如果和上个儿子的顶端一样,并入 now;否则将 now 并入 lst,然后清空 now 再并入。

现在就是查询的问题了。

如果这条链长度是 len,那么另一半长度就要求在 [llen,rlen] 内。

显然是一个区间查询问题,因为最值不能容斥,硬上线段树。

然后简单做就行了。

实现细节

首先是一个很关键的答案组成问题。

我们查询线段树的时候,如果长度不是从 0 开始,就需要将一条链组成答案的情况特判。

这个表现在样例一,但测试数据内没有 hack 这个问题……

写了三天的 lq 不过样例,一怒之下提交 AC。

然后就是线段树区间查询问题,记得跟上下界取 minmax

线段树涉及到多次清空的问题,动态开点的话可以在新建节点时操作,固定结构的话就用一个 lazytag,访问的时候下传标记即可。

还有两棵权值线段树的合并,这个题里面可以直接用 std::vector 存进行过的操作,然后重新插入一次,毕竟多码多错,少码少错,不码不错

Code:

经典的放一半,就问你气不气

实在需要拿去对拍啥的也可以找我,虽然我也想不出怎么找我

    SegmentTree lst,now;//lst 和 now 的作用见上
    std::vector<std::pair<int,long long>> vec;
    std::vector<std::pair<int,long long>> pos;
	//vec 是当前子树,pos 是当前所有同色的子树
    std::vector<edge> son;//存儿子
    void work_calc(int u,int fa,int col,int dep,long long dis){
        vec.emplace_back(dep,dis);//递归计算深度与权值
        for(edge e:G[u])
            if(!vis[e.ver]&&e.ver!=fa)
                work_calc(e.ver,u,e.col,dep+1,dis+(e.col==col?0:val[e.col]));
    }
    void solve(int u){
        vis[u]=1;
        son.clear();
        for(edge e:G[u])
            if(!vis[e.ver])
                son.emplace_back(e);
        std::sort(son.begin(),son.end(),[](edge x,edge y)->bool{
            return x.col<y.col;
        });//排序
        //lambda 表达式,https://oi-wiki.org/lang/lambda/ 有介绍
        int lst_col={-1};//上一个颜色
        lst.init(all);//链的长度不会超过根的 size
        now.init(all);
        pos.clear();
        for(auto e:son){
            if(~lst_col){
                vec.clear();
                work_calc(e.ver,u,e.col,1,val[e.col]);//计算链
                if(e.col==lst_col){//颜色相同
                    for(auto it:vec)
                        ans=std::max({ans,l<=it.first&&it.first<=r?it.second:-Inf,lst.query(l-it.first,r-it.first)+it.second,now.query(l-it.first,r-it.first)+it.second-val[e.col]});//查询
                    for(auto it:vec)
                        now.updata(it.first,it.second),pos.emplace_back(it.first,it.second);//更新
                }
                else{//新颜色
                    for(auto it:pos)
                        lst.updata(it.first,it.second);//此时之前的 now 等效于 lst
                    pos.clear();
                    now.init(all);
                    for(auto it:vec)
                        ans=std::max({ans,l<=it.first&&it.first<=r?it.second:-Inf,lst.query(l-it.first,r-it.first)+it.second});
                    for(auto it:vec)//清空后的新数据
                        now.updata(it.first,it.second),pos.emplace_back(it);
                }
                lst_col=e.col;
            }
            else{//特殊处理第一棵
                vec.clear();
                work_calc(e.ver,u,e.col,1,val[e.col]);
                for(auto it:vec)
                    ans=std::max(ans,l<=it.first&&it.first<=r?it.second:-Inf);
                for(auto it:vec)
                    now.updata(it.first,it.second),pos.emplace_back(it);
                lst_col=e.col;
            }
        }
        //...递归继续处理
    }
posted @   LQ636721  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示