Typesetting math: 100%
耿耿星河|

MontesquieuE

园龄:4年2个月粉丝:56关注:26

2020-11-02 12:44阅读: 89评论: 0推荐: 0

【题解】【集训队作业2018】三角形

题目链接

真就讲完套路啥都简单……

题面

S 有一棵 nn 个点的有根树,每个点有权值 wiwi ,初始每个结点上都没有石子。

S 准备了一些石子,并把它们拿在手中。她可以进行以下两种操作任意多次:

  1. 从手中取 wiwi 个石子放在结点 ii 上,进行该操作要求结点 ii 的所有孩子 jj 上都有 wjwj 个石子。
  2. 将结点 ii 上的所有石子收回手中。

T 想知道对于每个 ii ,为了在结点 ii 上放 wiwi 个石子,S 至少需要准备多少石子。

思路

显然,当你往一个节点放石子的时候,每个儿子节点都是放满的。

那么每一次操作可以用一个二元组表示: (wiwson,wson)(wiwson,wson) ,表示这一次操作完成后手中石子的增量,和这次操作中石子数达到的最大值。

由于 “父亲受多个儿子限制” 很难处理,考虑把整个操作序列倒过来处理,那么一个点的限制就只有它的父亲。二元组变成了 (wsonwi,wson)(wsonwi,wson) ,需要最小化这个序列的历史最值。

考虑贪心,对每个操作求出它的优先度。

对于 x<0x<0 的情况,显然放在前面更优(使前缀更小),按 yy 升序;

如果 x0x0 ,试比较 (x,y)(x,y)(x,y)(x,y) 的优劣:max(y,y+x)<max(y,x+y)max(y,y+x)<max(y,x+y)

(y+x)<(x+y)=>xy>xy(y+x)<(x+y)=>xy>xy

如此就得到了全局最优的序列。

现在,每次找最优的一个,如果这个点的父亲还没处理,那么就和父亲合并即可(一旦做了父亲就做,因为优先级高)。注意到每棵子树的最优序列是全局的子序列,线段树合并得到答案。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
struct  node            //二元组
{
        ll x,y; int head,tail;
        node operator + ( const node &tmp ) const { return (node){x+tmp.x,max(y,x+tmp.y),head,tmp.tail}; }
        bool operator < ( const node &tmp ) const            
        {
                int t1=(x>=0),t2=(tmp.x>=0);
                if( t1!=t2 ) return x<tmp.x;
                if ( !t1 ) 
                        if ( y!=tmp.y ) return y<tmp.y;
                        else return head<tmp.head;
                if ( (y-x)!=(tmp.y-tmp.x) ) return (y-x)>(tmp.y-tmp.x);
                return head<tmp.head;
        }
}lis[N];

struct SegmentTree
{
        node x; int l,r;
}tr[N*40];
int f[N],n,id[N],fa[N],tr2[N],tot,nxt[N];
ll w[N],sum[N],ans[N],val[N];
bool vis[N];
vector<int> ve[N];
set<node> s;

int find( int x ) { return x==f[x] ? x : f[x]=find(f[x]); }

int merge( int u,int v )
{
        if ( !u || !v ) return u | v;
        tr[u].l=merge( tr[u].l,tr[v].l ); tr[u].r=merge( tr[u].r,tr[v].r );
        tr[u].x=tr[tr[u].l].x+tr[tr[u].r].x;
        return u;
}

void insert( int &cnt,int l,int r,int x,int id )
{
        cnt=++tot;
        if ( l==r ) { tr[cnt].x=(node){val[id],sum[id],l,l}; return; }
        int mid=(l+r)>>1;
        if ( x<=mid ) insert( tr[cnt].l,l,mid,x,id );
        else insert( tr[cnt].r,mid+1,r,x,id );
        tr[cnt].x=tr[tr[cnt].l].x+tr[tr[cnt].r].x;
}

void dfs( int u,int fa )
{
        insert( tr2[u],1,n,id[u],u );
        for ( vector<int> :: iterator it =ve[u].begin(); it!=ve[u].end(); it++ )
        {
                int v=*it; dfs( v,u ); tr2[u]=merge( tr2[u],tr2[v] );
        }
        ans[u]=tr[tr2[u]].x.y+w[u];
}

int main()
{
        int cas; scanf( "%d%d",&cas,&n );
        for ( int i=2; i<=n; i++ )
                scanf( "%d",&fa[i] ),ve[fa[i]].push_back(i);
        for ( int i=1; i<=n; i++ )
                scanf( "%lld",&w[i] ),sum[fa[i]]+=w[i],f[i]=i;
        
        for ( int i=1; i<=n; i++ )
        {
                val[i]=sum[i]-w[i]; lis[i]=(node){val[i],sum[i],i,i};
                s.insert(lis[i]);
        }
        vis[0]=1; int now=0;
        while ( !s.empty() )
        {
                set<node>:: iterator it=s.begin(); node x=*it; s.erase(it);
                if ( vis[fa[x.head]] )             //父亲节点已经处理过了
                {
                        nxt[now]=x.head; 
                        while ( now!=x.tail ) vis[now]=1,now=nxt[now];
                        vis[now]=1;
                }
                else                    //否则和父亲合并
                {
                        int pf=find( fa[x.head] ); s.erase( lis[pf] );
                        nxt[lis[pf].tail]=lis[x.head].head; lis[pf]=lis[pf]+lis[x.head];
                        f[find(x.head)]=pf; s.insert( lis[pf] );
                }       
        }
        int cnt=0; now=0;
        for ( int i=1; i<=n; i++ )
                cnt++,now=nxt[now],id[now]=i;
        dfs( 1,0 );

        for ( int i=1; i<=n; i++ )
                printf( "%lld ",ans[i] );
}

本文作者:MontesquieuE

本文链接:https://www.cnblogs.com/UntitledCpp/p/13913841.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MontesquieuE  阅读(89)  评论(0编辑  收藏  举报
编辑推荐:
· 你所不知道的 C/C++ 宏知识
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· SQL Server 内存占用高分析
· .NET Core GC计划阶段(plan_phase)底层原理浅谈
· .NET开发智能桌面机器人:用.NET IoT库编写驱动控制两个屏幕
阅读排行:
· 我干了两个月的大项目,开源了!
· 推荐一款非常好用的在线 SSH 管理工具
· 千万级的大表,如何做性能调优?
· 盘点!HelloGitHub 年度热门开源项目
· Phi小模型开发教程:用C#开发本地部署AI聊天工具,只需CPU,不需要GPU,3G内存就可以运行,
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 Like A Star(piano ver) Youngso Kim
Like A Star(piano ver) - Youngso Kim
00:00 / 00:00
An audio error has occurred.