Codeforces 1019E Raining season 点分治+multiset维护动态凸包

1 篇文章 0 订阅
1 篇文章 0 订阅

传送门ber~

题目大意及题解
cf教会我c++11

我的代码:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#include<vector>
#include<set>
#define int long long
#define INF 2147483647
#define pll pair<long long,long long>
#define mp make_pair
#define pb push_back
#define sd signed
#define fs first
#define sn second
#define N 100020
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
typedef long long LL;
int n,m,x,y,tk,tb,top;
int fir[N],siz[N];
bool b[N];
vector<pll> v;
struct Edge{
    int to,nex,k,b;
    Edge(){}
    Edge(int to,int nex,int k,int b):to(to),nex(nex),k(k),b(b){}
}nex[N<<1];
inline void add(int x,int y,int k,int b){
    nex[++top]=Edge(y,fir[x],k,b);
    fir[x]=top;
}
void GetSize(int x,int fa){
    siz[x]=1;
    for(int i=fir[x];i;i=nex[i].nex){
        if(nex[i].to==fa || b[nex[i].to]) continue;
        GetSize(nex[i].to,x);
        siz[x]+=siz[nex[i].to];
    }
}
int GetRoot(int x,int fa,int sum){
    int tmp=0;
    for(int i=fir[x];i;i=nex[i].nex){
        if(b[nex[i].to] || nex[i].to==fa) continue;
        if(siz[nex[i].to]>siz[tmp]) tmp=nex[i].to;
    }
    return (siz[tmp]>sum/2)?GetRoot(tmp,x,sum):x;
}
struct Line{
    mutable LL k,b,p;
    Line(){}
    Line(LL k,LL b,LL p):k(k),b(b),p(p){}
    bool operator < (const Line &a) const{
        return k<a.k;
    }
};
struct Data : multiset<Line>{
    inline bool check(iterator x,iterator y){
        if(y==end()){
            x->p=INF;
            return 0;
        }
        if(x->k==y->k) x->p=(x->b>y->b?INF:-INF);
        else x->p=(y->b-x->b)/(x->k-y->k)-(((y->b-x->b)^(x->k-y->k))<0)*(((y->b-x->b)%(x->k-y->k))!=0);
        return x->p>=y->p;
    }
    inline void add(LL k,LL b){
        auto t=insert(Line(k,b,0));
        auto nex=t;
        ++nex;
        while(check(t,nex)) nex=erase(nex);
        auto x=t;
        if(x!=begin() && check(--x,t)){
            t=erase(t);
            check(x,t);
        }
        t=x;
        while(t!=begin() && (--x)->p>=t->p){
            t=erase(t);
            check(x,t);
            t=x;
        }
    }
}tmp,ans;
struct pp{
    int k,b,p;
    pp(){}
    pp(int k,int b,int p):k(k),b(b),p(p){}
}a[N];
void GetRoute(int x,int fa,LL sumk,LL sumb){
    bool t=false;
    for(int i=fir[x];i;i=nex[i].nex){
        if(nex[i].to==fa || b[nex[i].to]) continue;
        t=true;
        GetRoute(nex[i].to,x,sumk+nex[i].k,sumb+nex[i].b);
    }
    if(!t) tmp.add(sumk,sumb);
}
inline void update(Data a,Data b){
    static vector<pll>q1,q2;
    q1.clear();q2.clear();
    for(auto it:a){
        if(!q1.empty() && it.k==q1[q1.size()-1].fs){
            q1[q1.size()-1].sn=max(q1[q1.size()-1].sn,it.b);
            continue;
        }
        q1.pb(mp(it.k,it.b));
    }
    for(auto it:b){
        if(!q2.empty() && it.k==q2[q2.size()-1].fs){
            q2[q2.size()-1].sn=max(q2[q2.size()-1].sn,it.b);
            continue;
        }
        q2.pb(mp(it.k,it.b));
    }
    static int l,r;
    l=r=0;
    while(l<(sd)q1.size() && r<(sd)q2.size()){
        v.pb(mp(q1[l].fs+q2[r].fs,q1[l].sn+q2[r].sn));
        if(l+1==(sd)q1.size()) r++;
        else if(r+1==(sd)q2.size()) l++;
        else{
            double p1=-(double)(q1[l+1].sn-q1[l].sn)/(double)(q1[l+1].fs-q1[l].fs);
            double p2=-(double)(q2[r+1].sn-q2[r].sn)/(double)(q2[r+1].fs-q2[r].fs);
            if(p1<p2) l++;
            else r++;
        }
    }
}
Data Merge(int x,int y,vector<Data> &q){
    if(x==y){
        Data tmp;
        tmp.add(0,0);
        update(tmp,q[x]);
        return q[x];
    }
    else{
        int mid=(x+y)>>1;
        Data t1=Merge(x,mid,q);
        Data t2=Merge(mid+1,y,q);
        update(t1,t2);
        for(auto it:t1)
            t2.add(it.k,it.b);
        return t2;
    }
}
void solve(int x){
    b[x]=true;
    vector<Data>q;
    for(int i=fir[x];i;i=nex[i].nex){
        if(b[nex[i].to]) continue;
        tmp.clear();
        tmp.add(0,0);
        GetRoute(nex[i].to,x,nex[i].k,nex[i].b);
        q.push_back(tmp);
    }
    if(!q.empty()) Merge(0,q.size()-1,q);
    for(int i=fir[x];i;i=nex[i].nex){
        if(b[nex[i].to]) continue;
        GetSize(nex[i].to,0);
        solve(GetRoot(nex[i].to,x,siz[nex[i].to]));
    }
}
main(){
    n=read();m=read();
    for(int i=1;i<n;i++){
        x=read();y=read();tk=read();tb=read();
        add(x,y,tk,tb);add(y,x,tk,tb);
    }
    GetSize(1,0);
    solve(GetRoot(1,0,siz[1]));
    ans.add(0,0);
    for(auto it:v) ans.add(it.fs,it.sn);
    top=0;
    for(auto it:ans) a[++top]=pp(it.k,it.b,it.p);
    for(int i=0,j=1;i<m;i++){
        while(a[j].p<i) j++;
        printf("%lld ",a[j].k*i+a[j].b);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值