[HNOI2015]开店(树剖+主席树+标记永久化)

听说正解点分树?我不会就对了

此题是 \([LNOI2014]LCA\) 强化版,也是差分一下,转化为区间加区间和

不过权值有大小要求,那么我们按照权值排序,依次加入主席树,询问的时候 \(lower\_bound\) 一下找到区间 \([l,r]\)

在主席树上实现区间加,肯定要标记永久化。每次最多修改 \(2\lfloor \log^n\rfloor\) 个区间,所以一次最多会开出 \(4\lfloor \log^n\rfloor-1\) 个结点,空间复杂度理论上是 \(O(4n\log^2n)\),时间复杂度 \(O(n\log^2 n)\)

但是神奇的是,在 \(n\leq 150000\) 的时候,我空间只开了 \(100\) 倍就过了(雾

\(Code\ Below:\)

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define mp make_pair
#define F first
#define S second
using namespace std;
const int maxn=150000+10;
const int inf=0x3f3f3f3f;
int n,q,A,val[maxn],head[maxn],tot;pii a[maxn];
int dep[maxn],top[maxn],siz[maxn],son[maxn],fa[maxn],id[maxn],tim;
int T[maxn],L[maxn*100],R[maxn*100],cnt;ll dis[maxn],sumE[maxn],sumdis[maxn],sum[maxn*100],lazy[maxn*100];

struct node{
    int to,next,val;
}e[maxn<<1];

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}

inline void addedge(int x,int y,int w){
    e[++tot].to=y;
    e[tot].val=w;
    e[tot].next=head[x];
    head[x]=tot;
}

void dfs1(int x,int f){
    siz[x]=1;fa[x]=f;
    dep[x]=dep[f]+1;
    int maxson=-1;
    for(int i=head[x],y;i;i=e[i].next){
        y=e[i].to;
        if(y==f) continue;
        val[y]=e[i].val;
        dis[y]=dis[x]+e[i].val;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(siz[y]>maxson){
            maxson=siz[y];
            son[x]=y;
        }
    }
}

void dfs2(int x,int topf){
    id[x]=++tim;
    sumE[tim]=val[x];
    top[x]=topf;
    if(son[x]) dfs2(son[x],topf);
    for(int i=head[x],y;i;i=e[i].next){
        y=e[i].to;
        if(y==fa[x]||y==son[x]) continue;
        dfs2(y,y);
    }
}

void update(int &now,int pre,int Le,int Ri,int l,int r){
    now=++cnt;L[now]=L[pre];R[now]=R[pre];lazy[now]=lazy[pre];
    sum[now]=sum[pre]+(sumE[min(Ri,r)]-sumE[max(Le,l)-1]);
    if(Le <= l && r <= Ri){
        lazy[now]++;
        return ;
    }
    int mid=(l+r)>>1;
    if(Le <= mid) update(L[now],L[pre],Le,Ri,l,mid);
    if(Ri > mid) update(R[now],R[pre],Le,Ri,mid+1,r);
}

ll query(int u,int v,int Le,int Ri,int l,int r){
    if(Le <= l && r <= Ri) return sum[v]-sum[u];
    int mid=(l+r)>>1;ll ans=(lazy[v]-lazy[u])*(sumE[min(Ri,r)]-sumE[max(Le,l)-1]);
    if(Le <= mid) ans+=query(L[u],L[v],Le,Ri,l,mid);
    if(Ri > mid) ans+=query(R[u],R[v],Le,Ri,mid+1,r);
    return ans;
}

inline void modify(int i,int x){
    T[i]=T[i-1];
    while(top[x]!=1){
        update(T[i],T[i],id[top[x]],id[x],1,n);
        x=fa[top[x]];
    }
    update(T[i],T[i],1,id[x],1,n);
}

inline ll ask(int u,int v,int x){
    ll ans=0;
    while(top[x]!=1){
        ans+=query(T[u],T[v],id[top[x]],id[x],1,n);
        x=fa[top[x]];
    }
    ans+=query(T[u],T[v],1,id[x],1,n);
    return ans;
}

int main()
{
    n=read(),q=read(),A=read();
    int x,y,w;
    for(int i=1;i<=n;i++) a[i]=mp(read(),i);
    for(int i=1;i<n;i++){
        x=read(),y=read(),w=read();
        addedge(x,y,w);addedge(y,x,w);
    }
    dfs1(1,0);dfs2(1,1);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++) sumE[i]+=sumE[i-1],sumdis[i]=sumdis[i-1]+dis[a[i].S];
    for(int i=1;i<=n;i++) modify(i,a[i].S);
    int u,l,r;ll lastans=0;
    while(q--){
        u=read(),l=read(),r=read();
        x=(l+lastans)%A;y=(r+lastans)%A;
        if(x>y) swap(x,y);
        l=lower_bound(a+1,a+n+1,mp(x,0))-a;
        r=upper_bound(a+1,a+n+1,mp(y,inf))-a-1;
        printf("%lld\n",lastans=dis[u]*(r-l+1)+(sumdis[r]-sumdis[l-1])-2*ask(l-1,r,u));
        lastans%=A;
    }
    return 0;
}

转载于:https://www.cnblogs.com/owencodeisking/p/10225992.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值