九条可怜是一个热爱阅读的女孩子。

这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣。

这个世界有 \(n\) 个城市,这 \(n\) 个城市被恰好 \(n-1\) 条双向道路联通,即任意两个城市都可以互相到达。同时城市 \(1\) 坐落在世界的中心,占领了这个城市就称霸了这个世界。

在最开始,这 \(n\) 个城市都不在任何国家的控制之下,但是随着社会的发展,一些城市会崛起形成国家并夺取世界的霸权。为了方便,我们标记第 \(i\) 个城市崛起产生的国家为第 \(i\) 个国家。在第 \(i\) 个城市崛起的过程中,第 \(i\) 个国家会取得城市 \(i\) 到城市 \(1\) 路径上所有城市的控制权。

新的城市的崛起往往意味着战争与死亡,若第 \(i\) 个国家在崛起中,需要取得一个原本被国家 \(j\)(\(j\ne i\))控制的城市的控制权,那么国家 \(i\) 就必须向国家 \(j\) 宣战并进行战争。

现在,可怜知道了,在历史上,第 \(i\) 个城市一共崛起了 \(a_i\) 次。但是这些事件发生的相对顺序已经无从考究了,唯一的信息是,在一个城市崛起称霸世界之前,新的城市是不会崛起的。

战争对人民来说是灾难性的。可怜定义一次崛起的灾难度为崛起的过程中会和多少不同的国家进行战争(和同一个国家进行多次战争只会被计入一次)。可怜想要知道,在所有可能的崛起顺序中,灾难度之和最大是多少。

同时,在考古学家的努力下,越来越多的历史资料被发掘了出来,根据这些新的资料,可怜会对 \(a_i\) 进行一些修正。具体来说,可怜会对 \(a_i\) 进行一些操作,每次会将 \(a_x\) 加上 \(w\)。她希望在每次修改之后,都能计算得到最大的灾难度。

然而可怜对复杂的计算并不感兴趣,因此她想让你来帮她计算一下这些数值。

对题面的一些补充:

  • 同一个城市多次崛起形成的国家是同一个国家,这意味着同一个城市连续崛起两次是不会和任何国家开战的:因为这些城市原来就在它的控制之下。
  • 在历史的演变过程中,第 $i$ 个国家可能会有一段时间没有任何城市的控制权。但是这并不意味着第 $i$ 个国家灭亡了,在城市 $i$ 崛起的时候,第 $i$ 个国家仍然会取得 $1$ 到 $i$ 路径上的城市的控制权。
  • 输入格式

    第一行输入两个整数 \(n,m\) 表示城市个数和操作个数。

    第二行输入 \(n\) 个整数表示 \(a_i\) 的初始值。

    接下来 \(n-1\) 行,每行输入两个整数 \(u_i,v_i\)(\(1\le u_i,v_i\le n\))描述了一条道路。

    接下来 \(m\) 行每行输入两个整数 \(x_i,w_i\) 表示将 \(a_{x_i}\) 加上 \(w_i\)。

    输出格式

    输出共 \(m+1\) 行,第一行表示初始的 \(a_i\) 的答案,接下来 \(m\) 行每行表示这次修正后的答案。

    样例一

    input

    5 3

    1 1 1 1 1

    1 2

    1 3

    2 4

    2 5

    2 1

    3 1

    4 1

    output

    6

    7

    9

    10

    explanation

    在修正开始之前,如果按照所在城市 \(4,1,5,3,2\) 的顺序崛起,那么依次会和 \(0,1,2,1,2\) 个国家进行战争。这时一共会产生 \(6\) 对敌对关系。可以证明这是所有崛起顺序中的最大值。

    样例二

    见样例数据下载。

    限制与约定

    测试点 $n$ $m$ 其他约定
    1 $\le 10$ $=0$ $a_i=1$
    2 $\le 150000$ $\le 150000$ 第 $i$ 条道路连接 $i$ 和 $i + 1$
    3
    4 $=0$
    5
    6 $\le 150000$
    7
    8
    9 $\le 4\times 10^5$ $\le 4\times 10^5$
    10

    对于 100% 的数据,\(1\le a_i,w_i\le 10^7\),\(1\le x_i\le n\)。

    时间限制:\(2\texttt{s}\)

    空间限制:\(512\texttt{MB}\)

    题解

    首先考虑静态询问答案,我们令 \(sum_i\) 表示 \(i\) 的子树的 \(a_v\) 之和,然后想要求这个点的儿子的子树中对当前点能产生的最大贡献

    正常情况下是:\(sum_i-1\),就是每次从不同儿子的子树中交错上来

    但是如果存在一个儿子 \(v\) 的 \(sum_v\) 大于 \(sum_i\) 的一半,那么我们就做不到每次交错地选,最后一定会有剩下的从同一子树上来,这样是无法产生贡献的(与同一个国家的战争只算一次贡献,这样的贡献在儿子处已经计算完了)。这样的情况下,答案是 \(2(sum_i-\max\{\max\{sum_v\},a_i\})\)

    这样我们就得到了一个 \(O(n)\) 的静态算法

    考虑动态的时候,我们发现修改一个点的 \(a_i\) 改变的就是这个点到根路径上的 \(sum_f\) 值,我们要对这些点进行修改。这不就想LCT的access吗,我们只要把重儿子的定义修改为存在上述第二种情况,然后每次修改的时候就可以重新计算贡献了

    #include<bits/stdc++.h>
    #define ui unsigned int
    #define ll long long
    #define db double
    #define ld long double
    #define ull unsigned long long
    #define ft first
    #define sd second
    #define pb(a) push_back(a)
    #define PII std::pair<int,int>
    #define PLL std::pair<ll,ll>
    #define mp(a,b) std::make_pair(a,b)
    #define ITR(a,b) for(auto a:b)
    #define REP(a,b,c) for(register int a=(b),a##end=(c);a<=a##end;++a)
    #define DEP(a,b,c) for(register int a=(b),a##end=(c);a>=a##end;--a)
    const int MAXN=400000+10;
    int n,m,hson[MAXN],e,beg[MAXN],nex[MAXN<<1],to[MAXN<<1];
    ll size[MAXN],a[MAXN],ans,val[MAXN];
    template<typename T> inline void read(T &x)
    {
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
    }
    template<typename T> inline void write(T x,char ch='\0')
    {
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(ch!='\0')putchar(ch);
    }
    template<typename T> inline bool chkmin(T &x,T y){return y<x?(x=y,true):false;}
    template<typename T> inline bool chkmax(T &x,T y){return y>x?(x=y,true):false;}
    template<typename T> inline T min(T x,T y){return x<y?x:y;}
    template<typename T> inline T max(T x,T y){return x>y?x:y;}
    inline void calc(int x)
    {
    ans-=val[x];
    val[x]=hson[x]?2*(size[x]-size[hson[x]]):size[x]-1;
    if(a[x]*2>size[x]+1)val[x]=2*(size[x]-a[x]);
    ans+=val[x];
    }
    #define lc(x) ch[(x)][0]
    #define rc(x) ch[(x)][1]
    struct LinkCut_Tree{
    int fa[MAXN],ch[MAXN][2],stk[MAXN],cnt;
    ll add[MAXN];
    inline bool nroot(int x)
    {
    return lc(fa[x])==x||rc(fa[x])==x;
    }
    inline void plus(int x,ll v)
    {
    size[x]+=v;add[x]+=v;
    }
    inline void pushdown(int x)
    {
    if(add[x])
    {
    if(lc(x))plus(lc(x),add[x]);
    if(rc(x))plus(rc(x),add[x]);
    add[x]=0;
    }
    }
    inline void rotate(int x)
    {
    int f=fa[x],p=fa[f],c=(rc(f)==x);
    if(nroot(f))ch[p][rc(p)==f]=x;
    fa[ch[f][c]=ch[x][c^1]]=f;
    fa[ch[x][c^1]=f]=x;
    fa[x]=p;
    }
    inline void splay(int x)
    {
    cnt=0;
    stk[++cnt]=x;
    for(register int i=x;nroot(i);i=fa[i])stk[++cnt]=fa[i];
    while(cnt)pushdown(stk[cnt--]);
    for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
    if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
    }
    inline int findroot(int x)
    {
    while(lc(x))pushdown(x),x=lc(x);
    return x;
    }
    inline void access(int x,int v)
    {
    for(register int y=0;x;x=fa[y=x])
    {
    splay(x);size[x]+=v;
    if(lc(x))plus(lc(x),v);
    if(hson[x])
    {
    cnt=0;
    stk[++cnt]=hson[x];
    for(register int i=hson[x];nroot(i);i=fa[i])stk[++cnt]=fa[i];
    while(cnt)pushdown(stk[cnt--]);
    if(size[hson[x]]*2<=size[x]+1)hson[x]=rc(x)=0;
    }
    int nrt=findroot(y);
    if(size[nrt]*2>size[x]+1)hson[x]=nrt,rc(x)=y;
    calc(x);
    }
    }
    };
    LinkCut_Tree T;
    #undef lc
    #undef rc
    inline void insert(int x,int y)
    {
    to[++e]=y;
    nex[e]=beg[x];
    beg[x]=e;
    }
    inline void dfs(int x,int f)
    {
    ll res=0;
    size[x]=a[x];T.fa[x]=f;
    for(register int i=beg[x];i;i=nex[i])
    if(to[i]==f)continue;
    else
    {
    dfs(to[i],x);
    size[x]+=size[to[i]];
    if(chkmax(res,size[to[i]]))hson[x]=to[i];
    }
    if(size[hson[x]]*2<=size[x])hson[x]=0;
    T.ch[x][1]=hson[x];
    calc(x);
    }
    int main()
    {
    read(n);read(m);
    REP(i,1,n)read(a[i]);
    REP(i,1,n-1)
    {
    int u,v;read(u);read(v);
    insert(u,v);insert(v,u);
    }
    dfs(1,0);
    printf("%lld\n",ans);
    while(m--)
    {
    int x,w;read(x);read(w);
    a[x]+=w;T.access(x,w);
    printf("%lld\n",ans);
    }
    return 0;
    }

    【刷题】UOJ #374 【ZJOI2018】历史的更多相关文章

    1. ZJOI2019一轮停课刷题记录

      Preface 菜鸡HL终于狗来了他的省选停课,这次的时间很长,暂定停到一试结束,不过有机会二试的话还是可以搞到4月了 这段时间的学习就变得量大而且杂了,一般以刷薄弱的知识点和补一些新的奇怪技巧为主. ...

    2. 【BZOJ5212】[ZJOI2018]历史(Link-Cut Tree)

      [BZOJ5212][ZJOI2018]历史(Link-Cut Tree) 题面 洛谷 BZOJ 题解 显然实际上就是给定了一棵树和每个点被\(access\)的次数,求解轻重链切换的最大次数. 先考 ...

    3. 洛谷P1926 小书童—刷题大军【01背包】

      题目链接:https://www.luogu.org/problemnew/show/P1926 题目背景 数学是火,点亮物理的灯:物理是灯,照亮化学的路:化学是路,通向生物的坑:生物是坑,埋葬学理的 ...

    4. 好像leeceode题目我的博客太长了,需要重新建立一个. leecode刷题第二个

      376. Wiggle Subsequence               自己没想出来,看了别人的分析. 主要是要分析出升序降序只跟临近的2个决定.虽然直觉上不是这样. 455. 分发饼干     ...

    5. OI刷题录——hahalidaxin

      16-3-25  —— bzoj 2049 [Sdoi2008]Cave 洞穴勘测:LCT入门 bzoj 2002 [Hnoi2010]Bounce 弹飞绵羊:LCT Tsinsen A1303. t ...

    6. 牛客网Java刷题知识点之Map的两种取值方式keySet和entrySet、HashMap 、Hashtable、TreeMap、LinkedHashMap、ConcurrentHashMap 、WeakHashMap

      不多说,直接上干货! 这篇我是从整体出发去写的. 牛客网Java刷题知识点之Java 集合框架的构成.集合框架中的迭代器Iterator.集合框架中的集合接口Collection(List和Set). ...

    7. LeetCode刷题感想

      断断续续用了半年的时间把LeetCode刷完了,之前复习了数据结构与算法.将刷题与复习数据结构结合起来会更有效果.总之不是为了刷题而刷题,而是为了巩固和补充一部分知识. LeetCode真的是一个很好 ...

    8. 洛谷 P1926 小书童——刷题大军

      题目背景 数学是火,点亮物理的灯:物理是灯,照亮化学的路:化学是路,通向生物的坑:生物是坑,埋葬学理的人. 文言是火,点亮历史宫灯:历史是灯,照亮社会之路:社会是路,通向哲学大坑:哲学是坑,埋葬文科生 ...

    9. AC日记——小书童——刷题大军 洛谷 P1926

      题目背景 数学是火,点亮物理的灯:物理是灯,照亮化学的路:化学是路,通向生物的坑:生物是坑,埋葬学理的人. 文言是火,点亮历史宫灯:历史是灯,照亮社会之路:社会是路,通向哲学大坑:哲学是坑,埋葬文科生 ...

    10. 洛谷—— P1926 小书童——刷题大军

      https://www.luogu.org/problem/show?pid=1926#sub 题目背景 数学是火,点亮物理的灯:物理是灯,照亮化学的路:化学是路,通向生物的坑:生物是坑,埋葬学理的人 ...

    随机推荐

    1. JavaWeb学习总结-12 JSTL标签语言

      一 JSTL JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能. JSTL支持通用的.结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签. 除了 ...

    2. Luogu3702 SDOI2017 序列计数 矩阵DP

      传送门 不考虑质数的条件,可以考虑到一个很明显的$DP:$设$f_{i,j}$表示选$i$个数,和$mod\ p=j$的方案数,显然是可以矩阵优化$DP$的. 而且转移矩阵是循环矩阵,所以可以只用第一 ...

    3. NOI.ac #31 MST DP、哈希

      题目传送门:http://noi.ac/problem/31 一道思路好题考虑模拟$Kruskal$的加边方式,然后能够发现非最小生成树边只能在一个已经由边权更小的边连成的连通块中,而树边一定会让两个 ...

    4. 该如何以正确的姿势插入SVG Sprites?

      大家好,我是苏南,今天要给大家分享的是SVG sprite(也叫雪碧图),所谓雪碧图,当然就不是我们常喝的雪碧饮料(Sprites)哦,哈哈- 当下流程的移动端,手机型号太多太多,今天工作项目中突然发 ...

    5. zookeeper 动态管理nginx配置

      假设我们有一个场景,所有服务器共享同一份配置文件,我们肯定不可能单独手动维护每台服务器,这时可以利用zookeeper的配置管理功能. 环境:python + nginx + zookeeper 目的 ...

    6. Docker容器学习梳理 - Dockerfile构建镜像

      在Docker的运用中,从下载镜像,启动容器,在容器中输入命令来运行程序,这些命令都是手工一条条往里输入的,无法重复利用,而且效率很低.所以就需要一 种文件或脚本,我们把想执行的操作以命令的方式写入其 ...

    7. 《Linux内核设计与实现》读书笔记三

      Chapter 18 调 试 18.1 准备开始 1.准备工作: 一个bug 一个藏匿bug的内核版本 相关内核代码的知识和运气 2.执行foo就会让程序立即产生核心信息转储(dump core). ...

    8. 20135337——linux第四次实践:字符集总结与分析

      ASCII & GB2312 & UTF-8 ASCII 主要用于显示现代英语和其他西欧语言.它是现今最通用的单字节编码系统,并等同于国际标准ISO 646: 7位(bits)表示一个 ...

    9. Flask-论坛开发-2-Jinja2模板

      对Flask感兴趣的,可以看下这个视频教程:http://study.163.com/course/courseLearn.htm?courseId=1004091002 1. Jinja2 模板介绍 ...

    10. shell脚本--变量与数组

      Linux中的变量有环境变量和用户自定义变量,关于环境变量,可以查看这篇博客:linux环境变量 本文主要针对的是用户在shell脚本中定义的变量,但是环境变量也可以在shell脚本中使用. 普通变量 ...