【HNOI2015】开店

Description

给出一棵二叉树,每个点有点权,多次询问所有点权在[l,r]范围内的点到x的距离和。
强制在线。
n<=1.5*1e5,q<=2*1e5,权值<=1e9

Solution

为什么年龄可以达到10^9呢?因为有紫asdfjawkejtwlkerj

这道题可以直接把虚树用线段树建出来,然后直接在虚树上乱搞,不过我很弱显然不会这种做法。
发现这棵树是二叉树耶,于是我们可以考虑一种又好写又能跑得过空间又小的做法:点分治!
考虑把点分树建出来,那么我就是需要对于每个点分树中的子树包含x的点统计它的子树中有多少条经过它到x的路径。
更一般一点我们需要求出某一个点的子树中除了某一个儿子的子树以外的点到x的距离和。
考虑我们求的是点分树,于是我们可以知道所有点的size之和为O(n log n)
所以我们可以暴力求出每一个点根它子树中的所有点,按年龄从小到大排序,查询这个点的时候直接二分即可。
发现这棵树是二叉树,所以我们只需要暴力开空间就够了,完全不虚。
不然可能还需要写一棵线段树emmmm

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;

typedef long long ll;

int read() {
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    int x=ch-'0';
    for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}

void write(ll x) {
    if (!x) {puts("0");return;}
    char ch[20];int tot=0;
    for(;x;x/=10) ch[++tot]=x%10+'0';
    fd(i,tot,1) putchar(ch[i]);
    puts("");
}

const int N=1.5*1e5+5,M=4*1e5+5;

int t[M],nxt[M],lst[N],v[M],num;
void add(int x,int y,int z) {
    t[++num]=y;v[num]=z;nxt[num]=lst[x];lst[x]=num;
}

int n,m,age[N],x,y,z,l,r,a,b,A;

struct P{int x,z;}p[N<<5];
bool cmp(P x,P y) {return age[x.x]<age[y.x];}

int size[N],Sz,rt,fa[N],L[N][3],R[N][3],cnt[N],son[N][3],tot;
bool bz[N];

void get_size(int x,int y) {
    size[x]=1;
    rep(i,x) if (!bz[t[i]]&&t[i]!=y) get_size(t[i],x),size[x]+=size[t[i]];
}

void find_root(int x,int y) {
    bool pd=1;
    rep(i,x) 
        if (!bz[t[i]]&&t[i]!=y) {
            find_root(t[i],x);
            if (size[t[i]]>Sz/2) pd=0;
        }
    if (Sz-size[x]>Sz/2) pd=0;
    if (pd) rt=x;
}

void dfs(int x,int y,int z) {
    p[++tot].x=x;p[tot].z=z;
    rep(i,x) if (!bz[t[i]]&&t[i]!=y) dfs(t[i],x,z+v[i]);
}

int solve(int x,int y) {
    get_size(x,0);
    Sz=size[x];find_root(x,0);
    int now=rt;
    rep(i,now) 
        if (!bz[t[i]]) {
            L[now][cnt[now]]=tot+1;
            dfs(t[i],now,v[i]);
            R[now][cnt[now]++]=tot;
        }
    int ret=0;bz[now]=1;fa[now]=y;
    rep(i,now) if (!bz[t[i]]) son[now][ret++]=solve(t[i],now);
    return now;
} 

int fir[N<<1],dfn[N<<1],dep[N],lg[N<<1],f[18][N<<1],sum[N],tmp;
ll pre[N<<5];

void travel(int x,int y) {
    fir[x]=++tmp;dfn[tmp]=x;dep[x]=dep[y]+1;
    rep(i,x) if (t[i]!=y) {sum[t[i]]=sum[x]+v[i];travel(t[i],x);dfn[++tmp]=x;}
}

int lca(int x,int y) {
    x=fir[x];y=fir[y];
    if (x>y) swap(x,y);
    int z=lg[y-x+1];
    if (dep[f[z][x]]<dep[f[z][y-(1<<z)+1]]) return f[z][x];
    else return f[z][y-(1<<z)+1];
}

int dist(int x,int y) {
    int z=lca(x,y);
    return sum[x]+sum[y]-2*sum[z];
}

int find(int l,int r,int v) {
    r++;
    while (l<r) {
        int mid=l+r>>1;
        if (age[p[mid].x]<=v) l=mid+1;
        else r=mid;
    }
    return l-1;
}

ll query(int x,int v) {
    ll res=0;
    for(int la=0,i=x;i;la=i,i=fa[i]) {
        int D=dist(x,i);
        res+=(age[i]<=v)*D;
        fo(j,0,cnt[i]-1) 
            if (son[i][j]!=la) {
                int pos=find(L[i][j],R[i][j],v);
                res+=(ll)(pos-L[i][j]+1)*D;
                res+=pre[pos]-pre[L[i][j]-1];
            }
    }
    return res;
}

int main() {
    freopen("shop.in","r",stdin);
    freopen("shop.out","w",stdout);
    n=read();m=read();A=read();
    fo(i,1,n) age[i]=read();
    fo(i,1,n-1) {
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    solve(1,0);
    fo(i,1,n) fo(j,0,cnt[i]-1) sort(p+L[i][j],p+R[i][j]+1,cmp);
    fo(i,1,tot) pre[i]=pre[i-1]+p[i].z;

    travel(1,0);
    fo(i,1,tmp) f[0][i]=dfn[i],lg[i]=log(i)/log(2);
    fo(j,1,17)
        fo(i,1,tmp-(1<<j))
            if (dep[f[j-1][i]]<dep[f[j-1][i+(1<<j-1)]]) f[j][i]=f[j-1][i];
            else f[j][i]=f[j-1][i+(1<<j-1)];

    ll ans=0;
    for(;m;m--) {
        x=read();a=read();b=read();
        l=min((a+ans)%A,(b+ans)%A);
        r=max((a+ans)%A,(b+ans)%A);
        write(ans=query(x,r)-query(x,l-1));
    }
    return 0;
}
  • 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、付费专栏及课程。

余额充值