【2016北京集训】银河战舰

Portal --> broken qwq

Description

  给你一棵树,每个节点有一个权值,求树上最长的一条链上的最长上升子序列长度

  数据范围:\(2<=n<=200000,1<=val<=10^6\),其中\(val\)表示点权

  

Solution

  深陷点分无法自拔论我的头里面都装了些什么== 大家好我是一个失去脑子的点分+线段树选手==

?  这题的话。。首先一个前置技能是nlogn子序列,然后我们只要把这个玩意搬到树上就好了

?  注意到假设答案过一个点\(x\),那么肯定是\(x\)的某个子树中的一段上升子序列+另一个子树中的一段下降子序列,所以我们需要维护两个东西

?  类似于序列上的求解,我们对于树上的每一个节点维护一个\(up\)数组和\(dw\)数组,\(up[i]\)表示该节点子树中长度为\(i\)的上升子序列(从深到浅)的深度最浅的那个值最小是多少,\(dw[i]\)类似地表示下降子序列深度最浅的那个值最大是多少

?  那么答案的求解应该就是对于每个点选择其两个不同的后继子树,其中一个取\(up\),另一个取\(dw\),拼在一起,然后还要分为取这个点或者不取这个点两种情况

?  然后对于一个节点的\(up\)和\(dw\)就是直接从子树中继承过来就好了,最后再加上单独自己这个点的情况

?  现在的问题是,我们要怎么实现

?  对于求解\(ans\)中那个“选择两个不同的子树”这个,我们可以用先求解再更新的套路来处理,每次dfs完一个儿子,我们先用这个儿子的信息和当前的合并了前面遍历的所有儿子信息的数组(汇总数组)进行答案求解,然后再将这个儿子的信息合并到那个汇总数组中

  合并信息的话,注意到合并两个信息数组的复杂度和该数组对应的子树深度有关,所以我们考虑启发式合并的思想,直接暴力将小的往大的里面插就好了

?  这种思想也会用在求解中,我们考虑暴力枚举较小的那个数组中的每个元素,然后在另一个数组中二分找到可以接上的最长长度,然后更新答案即可

?  最后是加入自己单独这个点:这部分的处理其实和普通的序列问题处理方式基本一致,也是二分一波然后直接更新就好了

?  再具体一点的话。。可以考虑用vector来存每个节点的\(up\)和\(dw\),不过这样需要注意一点就是下标和其对应的长度会差\(1\),这个偏移在计算的时候需要注意

  虽然不太会证明。。但是跟启发式合并类似,总的时间复杂度是\(O(nlogn)\)的

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=2*(1e5)+10;
struct xxx{
    int y,nxt;
}a[N*2];
vector<int> up[N],dw[N];
vector<int>::iterator it;
int h[N],mxdep[N],son[N],dep[N];
int val[N];
int n,m,tot,ans;
bool cmp(int x,int y){return x>y;}
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void merge(int x,int y){//into x
    int szx=up[x].size(),szy=up[y].size();
    if (szx>=szy)
        for (int i=0;i<szy;++i)
            up[x][i]=min(up[x][i],up[y][i]);
    else{
        for (int i=0;i<szx;++i)
            up[y][i]=min(up[y][i],up[x][i]);
        swap(up[x],up[y]);
    }

    szx=dw[x].size(); szy=dw[y].size();
    if (szx>=szy)
        for (int i=0;i<szy;++i)
            dw[x][i]=max(dw[x][i],dw[y][i]);
    else{
        for (int i=0;i<szx;++i)
            dw[y][i]=max(dw[y][i],dw[x][i]);
        swap(dw[x],dw[y]);
    }
}
void choose(int x,int y,int who){
    int len1=lower_bound(up[x].begin(),up[x].end(),val[who])-up[x].begin();
    int len2=lower_bound(dw[y].begin(),dw[y].end(),val[who],cmp)-dw[y].begin();
    ans=max(ans,len1+len2+1);
}
void nchoose(int x,int y,int who){
    int len,szx=up[x].size(),szy=dw[y].size();
    if (szx>=szy){
        for (int i=0;i<szy;++i){
            len=lower_bound(up[x].begin(),up[x].end(),dw[y][i])-up[x].begin();
            ans=max(ans,(i+1)+len);
        }
    }
    else{
        for (int i=0;i<szx;++i){
            len=lower_bound(dw[y].begin(),dw[y].end(),up[x][i],cmp)-dw[y].begin();
            ans=max(ans,(i+1)+len);
        }
    }
}
void get_ans(int x,int u){
    int len1,len2;
    choose(x,u,x);
    choose(u,x,x);

    nchoose(x,u,x);
    nchoose(u,x,x);
}
void insert(int x){
    int pos,sz=up[x].size();
    it=upper_bound(up[x].begin(),up[x].end(),val[x]);
    pos=it-up[x].begin();
    if (it==up[x].end()){
        if (!sz||up[x][sz-1]<val[x])
            up[x].push_back(val[x]);
    }
    else if (pos==0||up[x][pos-1]<val[x])
        up[x][pos]=val[x];

    sz=dw[x].size();
    it=upper_bound(dw[x].begin(),dw[x].end(),val[x],cmp);
    pos=it-dw[x].begin();
    if (it==dw[x].end()){
        if (!sz||dw[x][sz-1]>val[x])
            dw[x].push_back(val[x]);
    }
    else if (pos==0||dw[x][pos-1]>val[x])
        dw[x][pos]=val[x];
}
void dfs(int fa,int x){
    int u;
    for (int i=h[x];i!=-1;i=a[i].nxt){
        u=a[i].y;
        if (u==fa) continue;
        dfs(x,u);
        get_ans(x,u);
        merge(x,u);
    }
    insert(x);
    int debug=1;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    int x,y;
    scanf("%d",&n);
    memset(h,-1,sizeof(h));
    tot=0;
    for (int i=1;i<=n;++i) scanf("%d",val+i);
    for (int i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    ans=0;
    dfs(0,1);
    printf("%d\n",ans);
}

原文地址:https://www.cnblogs.com/yoyoball/p/9720896.html

时间: 2024-08-30 15:52:58

【2016北京集训】银河战舰的相关文章

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

【2016北京集训测试赛(八)】直径

注意:时限更改为4s 题解 考虑最原始的直径求法:找到离根节点(或离其他任意一点)最远的节点far1,再从far1出发找到离far1最远的节点far2,far1至far2的距离即为直径. 题目中提到了将原树的子树复制成新子树这一操作,显然如果我们将子树真正复制出来是会爆炸的. 实际上我们可以将每棵新子树中,真正有用的节点提取出来,以简化每个子树的结构,再按照题目的要求连接各个新子树. 我们用虚树来重构每一棵子树.每棵子树的虚树的关键点应至少包含: 子树的根节点. 这棵子树内部的直径的两端节点.

【2016北京集训测试赛(七)】自动机 (思考题)

Time Limit: 1000 ms Memory Limit: 256 MB Description Solution 这是一道看起来令人毫无头绪的题,然而确实十分简单巧妙TAT. 题目要求所有点执行相同指令后都回到初始状态. 我们先来考虑只有两种状态的情况:初始状态$T_0$与另一个状态$T_x$. 这样,我们可以通过一个二元记忆化深搜,来得到一种方案A,使得$T_0$回到$T_0$,且$T_x$回到$T_0$.如果这个方案都不存在,那么此时无解. 现在我们知道,执行方案A后,$T_x$与

【2016北京集训测试赛】river

HINT 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. [吐槽] 嗯..看到这题的想法的话..先想到了每个点的度为2,然后就有点不知所措了 隐隐约约想到了网络流,但并没有继续往下想了... 听完学长的讲评之后(%xj)个人觉得建图还是很有意思的ovo [题解] 因为每个点到对面都有k种方式,那就想到每个点原来的点$x_0$拆成k个点$x_1$, $x_2$, $x_3$... $x_k$ 然后很自然地$x_0$和拆成的点之间要连边 容量的话,因为hint里面的限制

2016北京集训测试赛(十七)- 小结

先说一下总体的情况. 场上期望得分 50 + 40 + 30 = 120 , 最后得分 50 + 0 + 30 = 80 , 实际上自己能力所及能做到的 50 + 65 + 30 = 145 分. 第二题爆零是因为我开始写了一个做法, 后来发现这个做法是错的, 然后开始随机化, 调着调着突然发现只有一分钟了, 然后自己把之前调的改回来, 然后发现怎么全都输出 0 ??? Excuse me ?? 原本不用随机化的做法可以拿 40 分, 如果结合上暴力就有 65 了. 这几天打起比赛来还是暴露了许

【2016北京集训测试赛】azelso(unfinished)

[吐槽] 首先当然是要orzyww啦 以及orzyxq奇妙顺推很强qwq 嗯..怎么说呢虽然说之前零零散散做了一些概d的题目但是总感觉好像并没有弄得比较明白啊..(我的妈果然蒟蒻) 这题的话可以说是难得的一道搞得比较清楚的概d题目吧记录一下还是挺有意思的ovo 当然咯..显然考场上并没有推出来..嗯qwq [题解] 看到说要求期望的距离,然后总的长度又被分成了一段一段的(各个事件) 所以就有一个比较直接的想法:将每一段期望走的次数算出来然后再乘上每一段的距离,加起来就是答案啦 那么现在问题来了怎

[2016北京集训测试赛(一)]奇怪的树-[树链剖分]

Description Solution 对于操作1,不论选了哪个点为a,最后反转颜色的点集都只有两种类型(显然啦). 暴力解法:对每个操作3,从a向上直到根节点,每到一个节点记录(它父亲的黑点数减去自己的黑点数)*父亲节点的编号.另外,还要记录a子树内的黑点.. 这种O(n2)的做法肯定会爆,考虑优化.由于这是一棵静态树,考虑树链剖分. 需要记录一个数组re[x][0/1][0/1].第2维表示深度的奇偶,第3维表示点的颜色.例如:re[x][0][0]记录的是初始情况下以x为根的子树中深度为

[2016北京集训测试赛(五)]打地鼠-[思考题]

Description Solution 我们先只考虑一只地鼠的情况,依题意得,在某一个时刻该地鼠的可能停留位置是一个公差为2的等差数列.我们设这个等差数列的两端为[L,R].则如果区间[L+1,R-1]的格子被打实际上是不会影响L和R的(列一个等差数列实际模拟一下就发现啦).而如果格子L被打,则L+2:如果格子R被打,则R-2.打了格子后,别忘了L--,R++. 嗯根据以上性质,我们可以知道,地鼠1,3,5,7,9...的L是非递减的,地鼠2,4,6,8,10...的L也是非递减的. 然后看一

【2016北京集训】Mushroom

Portal --> broken qwq Description 一开始有个蘑菇,蘑菇里面有\(n\)个房间,是一棵有根树,\(1\)号是根,每个房间里面都有杂草,现在要支持以下操作:将某个指定蘑菇复制一份作为一个新的蘑菇:将蘑菇\(v\)合并到蘑菇\(u\)中,有杂草的房间取并(合并后\(v\)不会消失):某个蘑菇子树除草/除子树外除草:某个蘑菇路径除草/除路径外除草:某个蘑菇标号为\(l\sim r\)房间除草/除了这些房间外除草:查询清除某个蘑菇上面所有杂草的时间:一单位时间内可以除最多