P3261 [JLOI2015]城池攻占有趣的做法

作者:\(lyca\) 蒟蒻丁
为什么标签里有栈,但是没有单调栈的做法呢
这题需要我们求出每个骑士能爬到的最高的城池,但是怎样快速查找符合条件的城池呢,这个时候我们想到了单调栈。如果这些城池非常平凡,我们就可以直接比大小。但是每经过一个城池骑士的属性就会被改变,所以我们不能使用单调栈。但是转念一想,暴力算法是把骑士强行推上去,累加后比大小,那么我们能不能把城池推上去呢。答案是可以。我们可以先让骑士和城池一起爬到根节点,再用他们修改后的值比大小,证明如下:

考虑一个骑士和一个城池,城池的防御值是 \(y\) ,骑士走到该城池的战斗力是 \(x\) ,若城池也走到根节点后权值为 \(ay+b\) ,那么骑士走到根节点后权值就是 \(ax+b\) ,而题目中保证了乘数一定大于零,所以二者的相对大小是不会改变的,所以我们拿走到根节点的终值来比较是正确的。

于是我们可以得出一个做法,先用一次搜索求出每个城池和骑士的终值,再用单调栈来求出每个骑士能走到的最高点(这是模板了,在弹栈的时候记录一下修改值),就可以顺利解决本题。

有趣的是,出题人可能没有想过这种做法,城池的终值会爆 \(long long\) ,所以终值的记录需要 \(int 128\),或者手写一些奇怪的压缩方式,需要注意的是不能直接取对数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=3e5+100;
__int128 h[N],f[N],tag1[N],tag2[N]={1,1},y;
ll n,m,head[N],cnt,st[N],ans1[N],ans2[N],top(1),pos[N],T[N],num[N];
ll a[N],dep[N],po;
bool pool[N];
vector<ll>vec[N];

struct star_platinum{
    ll to,nxt,a,b;
}q[N<<1];

inline void add(ll u,ll v,ll a1,ll a2){
    q[++cnt]={v,head[u],a1,a2},head[u]=cnt;
}

ll erf(__int128 x){
    ll l=1,r=top,ans;
    while(l<=r){
        ll mid=(l+r)>>1;
        if(f[st[mid]]>x)ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}

void pre(ll x,ll fa){/////////处理终值
    dep[x]=dep[fa]+1;
    f[x]=a[x]*tag2[x]+tag1[x];
    for(ll i=0;i<vec[x].size();i++)
        h[vec[x][i]]=h[vec[x][i]]*tag2[x]+tag1[x];
    for(ll i=head[x];i;i=q[i].nxt){
        ll v=q[i].to;
        if(q[i].a==1)tag2[v]=tag2[x]*q[i].b,tag1[v]=tag1[x];
        if(q[i].a==0)
            tag1[v]=tag1[x]+q[i].b*tag2[x],tag2[v]=tag2[x];
        pre(v,x);
    }
}

void dfs(ll x,ll fa){
    T[x]=top;
    po=erf(f[x]);
    top=po;
    pos[x]=st[top+1],num[x]=top+1;
    st[++top]=x;
    for(ll i=0;i<vec[x].size();i++){//////弹栈
        y=h[vec[x][i]],po=erf(y);
        ans1[st[po]]++,ans2[vec[x][i]]=dep[x]-dep[st[po]];
    }
    for(ll i=head[x];i;i=q[i].nxt){
        ll v=q[i].to;
        dfs(v,x);
    }
    st[num[x]]=pos[x];//////////回溯时还原栈
    top=T[x];
}

int main(){
    cin>>n>>m;
    for(ll i=1;i<=n;i++)cin>>a[i];
    for(ll i=2;i<=n;i++){
        ll a1,a2,a3;
	cin>>a1>>a2>>a3;
        add(a1,i,a2,a3);
    }
    for(ll i=1;i<=m;i++){
        ll a2,a1;
	cin>>a2>>a1;
        h[i]=a2;
        vec[a1].push_back(i);
    }
    f[0]=3e18;
    pre(1,0);
    dfs(1,0);
    for(ll i=1;i<=n;i++)printf("%lld\n",ans1[i]);
    for(ll i=1;i<=m;i++)printf("%lld\n",ans2[i]);
    return 0;
}
posted @ 2021-11-09 20:56  蒟蒻丁  阅读(56)  评论(0编辑  收藏  举报