BZOJ4012: [HNOI2015]开店 重链剖分 可持久化线段树

http://www.lydsy.com/JudgeOnline/problem.php?id=4012
两点间距离:深度之和-2×LCA深度
http://blog.csdn.net/mima_reincarnation/article/details/54024494
ORZ16年我就会的东西现在怎么忘没了。。。那题是离线排序做,那么对于这题用可持久化线段树来维护树链剖分就可以了。

#include<cstdio>
#include<algorithm>
#define gm 300001
using namespace std;
typedef long long ll;
struct Istream
{
    static const size_t str=1<<16;
    char buf[str],*s,*t;
    Istream():buf(),s(),t(){}
    char get()
    {
        return (s==t)?(t=buf+fread(s=buf,1,str,stdin),*s++):(*s++);
    }
    template<typename int_t>
    Istream& operator>> (int_t &x)
    {
        x=0; register char c;
        do c=get(); while(c<'0'||c>'9');
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=get();
        return *this;
    }
}cin;
struct Ostream
{
    static const size_t str=1<<16;
    char buf[str],*s,*t;
    Ostream():buf(),s(buf),t(buf+str){}
    ~Ostream(){fwrite(buf,1,s-buf,stdout);}
    void put(char c)
    {
        (s==t)?(fwrite(s=buf,1,str,stdout),*s++=c):(*s++=c);
    }
    template<typename int_t>
    Ostream& operator<< (int_t x)
    {
        if(!x) return operator<<('0');
        int a[22],t=1;
        while(x) a[t++]=x%10,x/=10;
        while(--t) put(a[t]+'0');
        return *this;
    }
    Ostream& operator<< (char c){return put(c),*this;}
}cout;
const char endl='\n';
int n,q,a;
struct pnt
{
    int pos,age;
    operator int() const {return age;}
}p[gm];
struct e
{
    int t;
    e *n;
    int c;
    e(int t,e *n,int c):t(t),n(n),c(c){}
}*f[gm];
int fa[gm],sz[gm],dep[gm],son[gm],len[gm],top[gm],dfn[gm],pre[gm],ct;
ll we[gm];
void dfs1(int x=1)
{
    sz[x]=1;
    for(e *i=f[x];i;i=i->n)
    {
        if(fa[x]==i->t) continue;
        fa[i->t]=x;
        len[i->t]=i->c;
        dep[i->t]=dep[x]+i->c;
        dfs1(i->t);
        if(sz[i->t]>sz[son[x]]) son[x]=i->t;
        sz[x]+=sz[i->t];
    }
}
void dfs2(int x=1)
{
    dfn[x]=++ct; pre[ct]=pre[ct-1]+len[x];
    top[x]=(x==son[fa[x]])?top[fa[x]]:x;
    if(son[x]) dfs2(son[x]);
    for(e *i=f[x];i;i=i->n)
    {
        if(fa[x]==i->t||son[x]==i->t) continue;
        dfs2(i->t);
    }
}
struct node
{
    node *l,*r;
    int mark,clk,tot;
    ll sum;
    node(int tot):l(),r(),mark(),clk(ct),tot(tot),sum(){}
    node(const node &x):l(x.l),r(x.r),mark(x.mark),clk(ct),tot(x.tot),sum(x.sum){}
    void cast()
    {
        ++mark; sum+=tot;
    }
    void up()
    {
        sum=ll(mark)*tot;
        if(l) sum+=l->sum;
        if(r) sum+=r->sum;
    }
}*rt[gm];
int x,y;
void insert(node *&o,int l=1,int r=n)
{
    if(!o) o=new node(pre[r]-pre[l-1]);
    else if(o->clk!=ct) o=new node(*o);
    if(x<=l&&r<=y) {o->cast(); return;}
    int mid=l+r>>1;
    if(x<=mid) insert(o->l,l,mid);
    if(mid<y) insert(o->r,mid+1,r);
    o->up();
}
ll query(node *o,int l=1,int r=n,int cnt=0)
{
    if(!o) return ll(pre[min(r,y)]-pre[max(l,x)-1])*cnt;
    if(x<=l&&r<=y) return o->sum+ll(o->tot)*cnt;
    cnt+=o->mark;
    int mid=l+r>>1;
    ll res=0;
    if(x<=mid) res+=query(o->l,l,mid,cnt);
    if(mid<y) res+=query(o->r,mid+1,r,cnt);
    return res;
}
void jump(int no)
{
    int b=p[no].pos;
    ++ct; rt[no]=rt[no-1];
    while(b)
    {
        x=dfn[top[b]],y=dfn[b];
        insert(rt[no]);
        b=fa[top[b]];
    }
}
ll get(int b,node *h)
{
    ll res=0;
    while(b)
    {
        x=dfn[top[b]],y=dfn[b];
        res+=query(h);
        b=fa[top[b]];
    }
    return res;
}
ll ans=0;
int main()
{
    cin>>n>>q>>a;
    for(int i=1;i<=n;++i)
    {
        p[i].pos=i;
        cin>>p[i].age;
    }
    sort(p+1,p+n+1);
    for(int i=1;i<n;++i)
    {
        int u,v,c; cin>>u>>v>>c;
        f[u]=new e(v,f[u],c); f[v]=new e(u,f[v],c);
    }
    dfs1(),dfs2(),ct=0;
    for(int i=1;i<=n;++i) we[i]=we[i-1]+dep[p[i].pos];
    for(int i=1;i<=n;++i) jump(i);
    for(int i=1;i<=q;++i)
    {
        int u; ll b,c; cin>>u>>b>>c;
        int l=min((b+ans)%a,(c+ans)%a),r=max((b+ans)%a,(c+ans)%a);
        l=lower_bound(p+1,p+n+1,l)-p,r=upper_bound(p+1,p+n+1,r)-p;
        if(l==r) ans=0;
        else ans=(we[r-1]-we[l-1])+ll(r-l)*dep[u]-(get(u,rt[r-1])-get(u,rt[l-1])<<1);
        cout<<ans<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值