题目

可以发现:由于只有加乘两种变化,并且乘的数是正的,那么占领同一城池的骑士,占领前和占领后战斗力相对大小是不变的 。

那么可以用左偏树维护战斗力从小到大的顺序,占领到同一城池的骑士。

显然每次考虑占领到这个城池的骑士时,要先考虑它管辖的城池的的骑士(DFS)。

之后得到了这些骑士战斗力构成的左偏树,每次找到战斗力最小值(直接找),如果战斗力不够就删除,表示它们失败了。

然后其它点就占领了这个城池。

打上修改战斗力的标记以后,把这棵左偏树合并到管辖它的城池上去,表示它们到达了管辖它们的城池。

合并次数$O(n)$,加入,删除次数$O(m)$,单次复杂度$O(\log n)$,总复杂度$O((n+m)\log n)$

当然由于这题管辖i的城池j一定满足j<i,那么可以直接从n到1考虑。

/*
Author: CNYALI_LK
LANG: C++
PROG: 3261.cpp
Mail: cnyalilk@vip.qq.com
*/
#include<bits/stdc++.h>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define DEBUG printf("Passing [%s] in LINE %lld\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %lld\n",__FUNCTION__,__LINE__)
#define all(x) x.begin(),x.end()
using namespace std;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
typedef pair<ll,ll> pii;
template<class T>ll chkmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>ll chkmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>T sqr(T a){return a*a;}
template<class T>T mmin(T a,T b){return a<b?a:b;}
template<class T>T mmax(T a,T b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
#define min mmin
#define max mmax
#define abs aabs
ll read(){
    ll s=0,base=1;
    char c;
    while(!isdigit(c=getchar()))if(c=='-')base=-base;
    while(isdigit(c)){s=s*10+(c^48);c=getchar();}
    return s*base;
}
ll w[333333],l[333333],r[333333],add_tag[333333],mul_tag[333333],rlink[333333],fa[333333],a[333333],v[333333],in[333333];
ll h[333333],die[333333],dis[333333],root[333333],dsum[333333];
void put_add(ll x,ll y){add_tag[x]+=y;w[x]+=y;}
void put_mul(ll x,ll y){add_tag[x]*=y;w[x]*=y;mul_tag[x]*=y;}
void push_tag(ll x){
    put_mul(l[x],mul_tag[x]);
    put_mul(r[x],mul_tag[x]);
    put_add(l[x],add_tag[x]);
    put_add(r[x],add_tag[x]);
    add_tag[x]=0;
    mul_tag[x]=1;
}
ll merge(ll x,ll y){
    if(!x)return y;
    if(!y)return x;
    push_tag(x);
    push_tag(y);
    if(w[x]>w[y])swap(x,y);
    r[x]=merge(r[x],y);
    if(rlink[r[x]]>rlink[l[x]])swap(l[x],r[x]);
    rlink[x]=rlink[r[x]]+1;
    return x;
}
void remove(ll &x){
    push_tag(x);
    x=merge(l[x],r[x]);
}

int main(){
#ifdef cnyali_lk
    freopen("3261.in","r",stdin);
    freopen("3261.out","w",stdout);
#endif
    ll n,m;
    n=read();
    m=read();
    for(ll i=1;i<=n;++i)h[i]=read();
    dis[0]=-1;
    for(ll i=2;i<=n;++i){fa[i]=read(),a[i]=read(),v[i]=read();dis[i]=dis[fa[i]]+1;}
    for(ll i=1;i<=m;++i){mul_tag[i]=1;w[i]=read();in[i]=read();root[in[i]]=merge(root[in[i]],i);}
    for(ll i=n;i;--i){
        while(root[i]&&w[root[i]]<h[i]){
            die[root[i]]=i;
            remove(root[i]);
            ++dsum[i];
        }   
        if(a[i])put_mul(root[i],v[i]);
        else put_add(root[i],v[i]);
        root[fa[i]]=merge(root[fa[i]],root[i]);
    }
    for(ll i=1;i<=n;++i)printf("%lld\n",dsum[i]);   
    for(ll i=1;i<=m;++i)printf("%lld\n",dis[in[i]]-dis[die[i]]);
    return 0;
}

标签: 左偏树

添加新评论