股神小D [点分治 or LCT]

题面

思路

点分治非常$naive$,不讲了,基本思路就是记录路径最小最大值.....然后没了

重点讲一下LCT的做法(好写不卡常)(点分一堆人被卡到飞起hhhh)

首先,这个路径限制由边限制决定,而树中的每条边都是割边

考虑一条边$i$,范围是$[l_i,r_i]$,那么当时间不在这个范围内的时候,这个边两边的点肯定不能跨过这条边有赚钱路径

那么,也就是说这一条边当且仅当时间在$[l_i,r_i]$范围内的时候生效

这样,我们可以考虑把边权范围限制变成一次加边和一次删边

我们把一条边根据加入删除的时间分成2条,并且把$2*(n-1)$条边按照时间排序

每次加入边的时候,统计这个边两边的联通块大小,乘起来加入答案

删边的时候就是把边删掉

这样子统计的话,我们容易发现,每条路径都只会被路径上加入最晚的那条边统计答案,不会有重复也不会有遗漏(不流失~不蒸发~)

这样就做完了,比点分治好写,而且跑的快【雾】

Code

依然是只提供LCT做法(实际是博主并没有写点分做法【逃】)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline int read(){
    int re=0,flag=1;char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
int n,fa[200010],ch[200010][2],siz[200010],vir[200010],rev[200010];
inline void update(int cur){
    siz[cur]=siz[ch[cur][0]]+siz[ch[cur][1]]+1+vir[cur];
}
inline bool nroot(int cur){return ((ch[fa[cur]][0]==cur)||(ch[fa[cur]][1]==cur));}
inline bool get(int cur){return ch[fa[cur]][1]==cur;}
inline void rotate(int cur){
    int f=fa[cur],ff=fa[f],son=get(cur),nr=nroot(f);
    ch[f][son]=ch[cur][son^1];
    if(ch[f][son]) fa[ch[f][son]]=f;
    ch[cur][son^1]=f;fa[f]=cur;
    fa[cur]=ff;
    if(nr) ch[ff][ch[ff][1]==f]=cur;
    update(f);update(cur);
}
void pushrev(int x){
    if(!x) return;
    swap(ch[x][0],ch[x][1]);
    rev[x]^=1;
}
void pushdown(int x){
    if(rev[x]){
        pushrev(ch[x][0]);
        pushrev(ch[x][1]);
        rev[x]=0;
    }
}
void push(int x){
    if(nroot(x)) push(fa[x]);
    pushdown(x);
}
void splay(int x){
    push(x);
    for(int f;nroot(x);rotate(x)){
        f=fa[x];
        if(nroot(f))
            rotate((get(x)==get(f))?f:x);
    }
}
void access(int x){
    for(int y=0;x;y=x,x=fa[x]){
        splay(x);
        vir[x]+=(siz[ch[x][1]]);
        ch[x][1]=y;
        vir[x]-=(siz[y]);
        update(x);
    }
}
void mroot(int x){
    access(x);splay(x);pushrev(x);
}
void cut(int x,int y){
    mroot(x);access(y);splay(y);
    ch[y][0]=0;fa[x]=0;update(y);
}
ll link(int x,int y){
    mroot(x);mroot(y);
    ll re=(ll)siz[x]*(ll)siz[y];
    fa[y]=x;vir[x]+=(siz[y]);update(x);
    return re;
}
struct edge{
    int u,v,w,f;
}a[400010];
inline bool cmp(edge l,edge r){
    if(l.w==r.w) return l.f<r.f;
    return l.w<r.w;
}
int main(){
    n=read();int i,t1,t2,t3,t4;ll ans=0;
    for(i=1;i<=n;i++) siz[i]=1,vir[i]=0,fa[i]=ch[i][0]=ch[i][1]=rev[i]=0;
    for(i=1;i<n;i++){
        t1=read();t2=read();t3=read();t4=read();
        a[i]=(edge){t1,t2,t3,0};
        a[n+i-1]=(edge){t1,t2,t4,1};
    }
    sort(a+1,a+(n<<1)-1,cmp);
    for(i=1;i<=((n-1)<<1);i++){
        if(!a[i].f) ans+=link(a[i].u,a[i].v);
        else cut(a[i].u,a[i].v);
    }
    printf("%lld\n",ans);
}

原文地址:https://www.cnblogs.com/dedicatus545/p/9713714.html

时间: 2024-08-25 23:07:06

股神小D [点分治 or LCT]的相关文章

股神小L 2016Vijos省选集训 day1

股神小L (stock.c/pas/cpp)============================ 小L厌倦了算法竞赛,希望到股市里一展身手.他凭借自己还行的计算机功底和可以的智商,成功建立一个模型预测了一支股票接下来n天的价格. 我们把这支股票第i天的价格称为a_i.在接下来n天里,每一天小L可以选择花费a_i买入一股或者卖出一股从而获得a_i元收入. 当然小L卖出股票的时候,自己的账户上必须要有至少一股的剩余.现在小L希望知道,在n天过去之后,采取最优策略的情况下自己最多赚到多少钱. 注意

[XSY 1556] 股神小D LCT维护子树信息

实现 1 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cctype> 6 #include <algorithm> 7 #include <vector> 8 using namespace std; 9 #define F(i, a, b) for (register int i = (a); i <= (b); i++)

百度在线考试编程题:股神小赛

小赛买股票了,本金为1元,第1天过后不变还是1元,从第二天开始涨1天跌1天,涨2天跌1天,涨3天跌1天,涨4天跌1天,……每次涨幅或跌幅都是1元,求第n天他还有多少钱(大致描述了下题目,具体我也不知道了) 自己比较笨的方法做的,相对高手来说代码量比较大(水平太low,大家见谅).代码如下: 1 /*********计算股价*********/ 2 /*********根据规律,将天数分成段数,计算股价和*********/ 3 #include <stdio.h> 4 #define Base

2016vijos 1-2 股神小L(堆)

维护前i天的最优解,那么在后面可能会对前面几天的买卖情况进行调整 如果前面买入,买入的这个在后面一定不会卖出 如果前面卖出,卖出的这个可能会在后面变成买入,因为买这个,卖后面的会获得更多的收益 用一个小根堆,存储前面所有的卖出的股票的价格 如果后面想卖出,扔到堆里 如果后面想买入,与堆顶元素比较,如果堆顶大,那就买入:如果堆顶小,那就把堆顶的卖出改为买入,后面那个卖出 即能卖就暂时先卖,扔到堆里,不优再调整 #include<queue> #include<cstdio> #inc

[2016北京集训试题14]股神小D-[LCT]

Description Solution 将(u,v,l,r)换为(1,u,v,l)和(2,u,v,r).进行排序(第4个数为第一关键字,第1个数为第二关键字).用LCT维护联通块的合并和断开.(维护联通块的大小,要维护虚边) 答案统计:每当四元组的第一个数为1(这时候合并点u,v所在连通块,反之拆开),在合并前ans+=size[u]*size[v]即可. Code #include<iostream> #include<cstdio> #include<cstring&g

股神小L [贪心]

题面 思路 股票题肯定是贪心或者$dp$啊 这个题比较$naive$,可以看出来你这里买股票的过程一定是能不买就不买,能卖就拣最贵的日子卖,而且时间不能倒流(废话= =||) 所以我们按照时间从前往后维护一个堆,表示你要卖股票的日子 每次访问到的时候,先把当前加进堆里 然后如果买的没有卖的多(也就是堆的size比已经过去的日子的一半多了),就买最便宜的那个 这样就做完了,300B良心题 Code #include<cstdio> #include<algorithm> #inclu

小股神助A股股民畅享经济发展红利

去年正当全球股市一片哀鸿遍野之时,中国A股市场高歌猛进,于2014年年底顺利攻破四千点指数大关,许多新股民为此也借炒股赚得盆满钵满.而老股民由于受到熊市思维惯性的影响,迟迟不敢踏入股市大门,为此白白错过了本轮A股的饕餮盛宴. 而与之相反,国内许多从未涉足过A股市场的股民,由于看到许多新股民进入股市后完全把股市当成了免费提款机,这些没有炒过股的普通百姓他们有些人也从银行取走存款投入股市也想从A股市场分得一杯羹,而那些没有存款的居民为此只有望股兴叹的份儿了! 不过2015年新年刚过,国内的很多投资机

1 股神

问题描述: 有股神吗? 有,小赛就是! 经过严密的计算,小赛买了一支股票,他知道从他买股票的那天开始,股票会有以下变化:第一天不变,以后涨一天,跌一天,涨两天,跌一天,涨三天,跌一天...依此类推. 为方便计算,假设每次涨和跌皆为1,股票初始单价也为1,请计算买股票的第n天每股股票值多少钱? 解题思路: 找规律:设 f(n)为第 n 天的股票钱. 由题意知   f(1)=1   f(2)=2     f(3)=1    f(4) =2   f(5)=3  ...  f(6)=2   ...  f

【编程题】股神

题目链接:股神 题目描述 有股神吗? 有,小赛就是! 经过严密的计算,小赛买了一支股票,他知道从他买股票的那天开始,股票会有以下变化:第一天不变,以后涨一天,跌一天,涨两天,跌一天,涨三天,跌一天...依此类推. 为方便计算,假设每次涨和跌皆为1,股票初始单价也为1,请计算买股票的第n天每股股票值多少钱? 附上本机没问题,但是提交不通过的Java代码....求指教 编译通过了....原来是我用的类名不对,平台要求统一用Main,而我的是Test... 1 import java.util.Sca