Codeforces 1023G:Pisces(最长反链)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35649707/article/details/82716869

传送门

题解:

相当于把观察排序,然后求一个最小链覆盖。
根据某D开头的定理,相当于是求最长反链。

考虑树形DP,一个方案在 v 的子树中为反链当且仅当:
1.在 v 的所有邻接点的子树中为反链。
2.存在一个时间 t 不能到达方案中的任意一个点。

因为每个点不能到达的时间是一个区间,我们可以维护 d p v , t 表示 v 的子树中公共时间为 t 的最优方案。因为重复的很多,我们可以只维护差分数组,当然可能不存在整数解,我们先把所有区间乘2保证有整数解。

每次往上转移长度 l 只需要向左右取个max即可。 相当于差分数组中正的往左移,负的往右移动。 两个差分点遇到则保留较大的那个,然后删除较小的,较大的值改为他们的和。 因为每次删除会丢掉一个点,这里是 O ( n log n ) 的。

只用考虑如果加入当前点 ( d , t ) 。 首先不加入就相当于直接左右取max,加入则只能加到 t 处,而且影响的区间只有 [ d l + 1 , d + l 1 ] ,这个处理的时候要注意一下。可以直接考虑连成两条边分别是 1 , l 1 分开考虑。

合并两个差分数组直接加入即可。 若用启发式合并,复杂度为 O ( n log 2 n )

#include <bits/stdc++.h>
using namespace std;
#define pii pair <int,int> 

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}

const int N=1e5+50;
const int INF=0x3f3f3f3f;

int n,q; int det[N]; 
vector <pii> edge[N];
vector <pii> ask[N];

struct Myset {
    int id;
    struct data {
        int first,second; int id;
        data(int first,int second,int id) : first(first), second(second), id(id) {}
        friend inline bool operator <(const data &a,const data &b) { 
            int posa=a.first+((a.second>0) ? -det[a.id] : det[a.id]),
                posb=b.first+((b.second>0) ? -det[b.id] : det[b.id]);
            if(posa!=posb) return posa<posb;
            return a.second<b.second;
        } 
    }; 
    typedef set <data> Set; 
    typedef Set :: iterator it;
    inline it prev(it t) {return --t;}
    inline it suff(it t) {return ++t;}
    Set *s;
    set <pii> *dis;
    inline int sgn(int x) {return (x>0) ? 1 : 0;}
    Myset() {s=new Set; dis=new set <pii>;}
    inline int getpos(it t) {return t->first+((t->second<0) ? det[t->id] : -det[t->id]);}
    inline void del_dis(it x,it y) {
        if(sgn(x->second) || !sgn(y->second)) return;
        dis->erase(dis->find(pii(y->first-x->first,y->first)));
    } 
    inline void inc_dis(it x,it y) {
        if(sgn(x->second) || !sgn(y->second)) return;
        dis->insert(pii(y->first-x->first,y->first));
    }
    inline void _erase(it x) {
        if(x!=s->begin()) del_dis(prev(x),x);
        if(x!=--s->end()) del_dis(x,suff(x));
        if(x!=s->begin() && x!=--s->end()) inc_dis(prev(x),suff(x));
        s->erase(x);
    }
    inline void add(int pos,int d) {
        if(!d) return;
        it nxt=s->lower_bound(data(pos-det[id],-INF,id));
        if(nxt==s->end() || getpos(nxt) !=pos) {
            if(s->size()>=2 && getpos(s->begin())<pos && getpos(--s->end())>pos) del_dis(prev(nxt),nxt); 
            data temp=data(pos+((d<0)?-det[id]:det[id]),d,id);
            it now=s->insert(temp).first;
            if(nxt!=s->end()) inc_dis(now,nxt);
            if(now!=s->begin()) inc_dis(prev(now),now);
        } else {
            data temp=*nxt; _erase(nxt);
            if(!(temp.second+d)) return;
            if(temp.second<0 && temp.second+d>0) temp.first+=2*det[id];
            if(temp.second>0 && temp.second+d<0) temp.first-=2*det[id]; 
            temp.second+=d;
            it nxt=s->insert(temp).first;
            if(nxt!=--s->end()) inc_dis(nxt,suff(nxt));
            if(nxt!=s->begin()) inc_dis(prev(nxt),nxt);
            if(nxt!=--s->end() && nxt!=s->begin()) del_dis(prev(nxt),suff(nxt));
        }
    }
    inline void md(int pos,int d) {add(pos,d); add(pos+1,-d);}
    inline void mv(int d) {
        if(!d) return;
        while(dis->size() && dis->begin()->first<=(d+det[id])*2) {
            it nxt=s->lower_bound(data(dis->begin()->second-2*det[id],-INF,id));
            if(nxt->second==-prev(nxt)->second) _erase(prev(nxt)), _erase(nxt);
            else if(nxt->second>-prev(nxt)->second) {
                int val=prev(nxt)->second;
                _erase(prev(nxt));
                add(nxt->first-det[id],val);
            } else {
                int val=nxt->second; nxt=prev(nxt);
                _erase(suff(nxt));
                add(nxt->first+det[id],val);
            }
        } det[id]+=d;
    }
    inline void merge(Myset &b) {
        if(s->size()<b.s->size()) swap(s,b.s), swap(id,b.id), swap(dis,b.dis);
        for(it v=b.s->begin();v!=b.s->end();++v) {
            data temp=*v; temp.first+=(v->second<0) ? det[b.id] : -det[b.id];
            add(temp.first,temp.second);
        } delete b.s; delete b.dis;
    }
    inline int getval(int pos) {
        it nxt=s->lower_bound(data(pos-det[id],-INF,id));   
        if(nxt!=s->end() && getpos(nxt)==pos) return nxt->second;
        return 0;
    }
} s[N];


inline void dfs(int x,int f) {
    for(auto v:edge[x]) {
        if(v.first==f) continue;
        dfs(v.first,x); 
        for(auto &d:ask[v.first]) {
            int delta=max(0,max(-s[v.first].getval(d.first),s[v.first].getval(d.first+1)));
            d.second=max(0,d.second-delta);
        }
        s[v.first].mv(1); 
        for(auto d:ask[v.first]) s[v.first].md(d.first,d.second);
        s[v.first].mv(v.second-1);
        s[x].merge(s[v.first]);
    }
}

int main() {
    n=rd();
    for(int i=1;i<=n;i++) s[i].id=i;
    for(int i=1;i<n;i++) {
        int x=rd(), y=rd(), w=rd()*2;
        edge[x].push_back(pii(y,w));
        edge[y].push_back(pii(x,w));
    } edge[0].push_back(pii(1,2)); 
    q=rd();
    for(int i=1;i<=q;i++) {
        int d=2*rd(), f=rd(), p=rd();
        ask[p].push_back(pii(d,f));
    } dfs(0,0);
    if(!s[0].s) {puts("0"); return 0;}
    int sum=0, mx=0;
    for(auto v=s[0].s->begin();v!=s[0].s->end();++v) {
        sum+=v->second; 
        mx=max(mx,sum);
    } cout<<mx<<'\n';
}

猜你喜欢

转载自blog.csdn.net/qq_35649707/article/details/82716869