• CF856D Masha and Cactus(树上 DP+抵消贡献技巧)


    CF856D Masha and Cactus

    我们先捞出一个根节点,那么一次旋变就是对路径上点的覆盖。

    \(dp_{i,0}\) 表示 \(i\) 没有选择时子树内最大收益,\(dp_{i,1}\) 表示 \(i\) 选择时子树内最大收益,那么将每条边存在 \(lca\) 上。

    之后贡献怎么算??我们需要快速计算不在根到子树内一点路径上,但与路径上的点直接相连的子树内点的权值之和。这里有个计算技巧:

    \(\color{yellow}{\bigstar\texttt{Trick}}\):设 \(g_i=-val_i+\sum_{j\in son_{i}}val_{j}\),那么上面的答案就是路径所有 \(g_i\) 之和加上根节点权值。

    查询路径可以转化为子树加、单点查询,树状数组解决即可。

    #define Maxn 400005
    #define Maxlogn 22
    int n,m,tot,Time;
    int hea[Maxn],nex[Maxn<<1],ver[Maxn<<1];
    int dfnl[Maxn],dfnr[Maxn],dep[Maxn],fa[Maxlogn][Maxn];
    ll f[Maxn],g[Maxn];
    struct EDGE
    {
    	int u,v,val;
    	EDGE(int U=0,int V=0,int Val=0):u(U),v(V),val(Val){}
    };
    vector<EDGE> e[Maxn];
    inline void add(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; }
    struct BIT
    {
    	ll tree[Maxn];
    	inline void add(int x,ll k){ while(x<=n) tree[x]+=k,x+=x&(-x); }
    	inline ll query(int x){ ll ret=0; while(x) ret+=tree[x],x-=x&(-x); return ret; }
    }T;
    inline int lca(int x,int y)
    {
    	if(dep[x]>dep[y]) swap(x,y);
    	for(int i=20;i>=0;i--) if(dep[fa[i][y]]>=dep[x]) y=fa[i][y];
    	if(x==y) return x;
    	for(int i=20;i>=0;i--) if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
    	return fa[0][x];
    }
    inline void addpath(int x,ll val) { T.add(dfnl[x],val),T.add(dfnr[x]+1,-val); }
    inline ll querypath(int x) { return T.query(dfnl[x]); }
    void dfs1(int x)
    {
    	dfnl[x]=++Time;
    	for(int i=hea[x];i;i=nex[i])
    	{
    		fa[0][ver[i]]=x,dep[ver[i]]=dep[x]+1;
    		for(int j=1;j<=20;j++) fa[j][ver[i]]=fa[j-1][fa[j-1][ver[i]]];
    		dfs1(ver[i]);
    	}
    	dfnr[x]=Time;
    }
    void dfs2(int x)
    {
    	ll sum=0;
    	for(int i=hea[x];i;i=nex[i]) dfs2(ver[i]),sum+=f[ver[i]];
    	for(EDGE v:e[x])
    	{
    		ll tmp=querypath(v.u)+querypath(v.v)+sum+v.val;
    		f[x]=max(f[x],tmp);
    	}
    	f[x]=max(f[x],sum),g[x]=sum-f[x],addpath(x,g[x]);
    }
    int main()
    {
    	n=rd(),m=rd();
    	for(int i=2,x;i<=n;i++) x=rd(),add(x,i);
    	dep[1]=1,dfs1(1);
    	for(int i=1,u,v,val,LCA;i<=m;i++)
    		u=rd(),v=rd(),val=rd(),LCA=lca(u,v),e[LCA].eb(u,v,val);
    	dfs2(1);
    	printf("%lld\n",f[1]);
    	return 0;
    }
    
  • 相关阅读:
    iOS 字典实现原理
    IOS中armv7,armv7s,arm64以及i386和x86_64讲解
    SDWebImage源码解析
    iOS Runtime的消息转发机制
    二叉树的遍历
    LINUX 常用命令 ps 详解
    LINUX 文件权限详解
    LINUX查看内存使用情况 free
    PHP isset() empty() isnull() 的区别
    PHP unset()函数销毁变量 但没有实现释放内存
  • 原文地址:https://www.cnblogs.com/EricQian/p/16585856.html
Copyright © 2020-2023  润新知