bzoj3052 糖果公园【树上带修改莫队】

解题思路:

传说中的b站毒瘤题,一交就引起民粪。
本来只是想做树上莫队模板bzoj3757苹果树的,结果题目下架了,只能上这道了……
如果不会树分块的可以先去做做bzoj1086,题解在这里
不会带修改莫队的可以先去做做bzoj2120,题解在这里

这里主要说一下正常无修改的树上莫队的做法。

先树分块,将每个询问按左端点所在块为第一关键字,右端点dfs序为第二关键字排序,这样就保证了指针挪动复杂度(证明方法同普通莫队)。

当前已知区间(x,y)表示x到y路径上除了lca(x,y)的所有点信息,对于每个节点记一个它是否已知的标记(lca标记为未知),这样对于下一个询问(tarx,tary),我们只需暴力把x指针挪到tarx,把y指针挪到tary,维护答案,并将路径上的标记取反,那么新的已知区间就是tarx到tary路径上除了lca(tarx,tary)的所有点了,所以单独算算lca的贡献即可。
不清楚的同学可以画画图手完一下,很明显的,详细证明可见这里

所以这道题我们在树上莫队的基础上套一个带修改莫队,排序第二关键字改为右端点所在块,修改是否在当前区间内可用x的标记直接判断。

细节见代码,还是很好懂的。

#include<bits/stdc++.h>
#define ll long long
using namespace std;  

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=100005;
int n,m,Q,S,qn,num,top;
int tot,first[N],dep[N],fa[N][20],nxt[N<<1],to[N<<1];
int c[N],belong[N],cnt[N],V[N],stk[N];
ll cur,W[N],ans[N];
bool exist[N];
struct node
{
    int x,y,bl,br,id;
    inline friend bool operator < (const node &a,const node &b)
    {
        if(a.bl!=b.bl)return a.bl<b.bl;
        if(a.br!=b.br)return a.br<b.br;
        return a.id<b.id;
    }
}q[N];
struct node1
{
    int x,val,ori;
}d[N];

void add(int x,int y)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y;
}

void dfs(int u)
{
    int now=top;
    for(int i=1;i<20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa[u][0])continue;
        fa[v][0]=u,dep[v]=dep[u]+1;
        dfs(v);
        if(top-now>=S)
        {
            ++num;
            while(top!=now)belong[stk[top--]]=num;
        }
    }
    stk[++top]=u;
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    int det=dep[x]-dep[y];
    for(int i=0;i<20;i++)if(det&(1<<i))x=fa[x][i];
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
} 

void rev(int x)
{
    cur-=W[cnt[c[x]]]*V[c[x]];
    exist[x]?--cnt[c[x]]:++cnt[c[x]];
    exist[x]=!exist[x];
    cur+=W[cnt[c[x]]]*V[c[x]];
}

void modify(int x,int y)
{
    if(!exist[x]){c[x]=y;return;}
    rev(x),c[x]=y,rev(x);
}

void upt(int curT,int tarT)
{
    while(curT<tarT)
    {
        ++curT;
        if(d[curT].x)modify(d[curT].x,d[curT].val);
    }
    while(curT>tarT)
    {
        if(d[curT].x)modify(d[curT].x,d[curT].ori);
        --curT;
    }
}

void solve(int x,int y)
{
    int t=lca(x,y);
    while(x!=t)rev(x),x=fa[x][0];
    while(y!=t)rev(y),y=fa[y][0];
}

int main()
{
    //freopen("lx.in","r",stdin);
    int op,x,y;
    n=getint(),m=getint(),Q=getint();S=pow(n,2.0/3);
    for(int i=1;i<=m;i++)V[i]=getint();
    for(int i=1;i<=n;i++)W[i]=getint()+W[i-1];
    for(int i=1;i<n;i++)
    {
        x=getint(),y=getint();
        add(x,y),add(y,x);
    }
    for(int i=1;i<=n;i++)c[i]=getint();
    dfs(1);
    for(int i=1;i<=Q;i++)
    {
        op=getint(),x=getint(),y=getint();
        if(op)
        {
            q[++qn].id=i,q[qn].x=x,q[qn].y=y,q[qn].bl=belong[x],q[qn].br=belong[y];
            if(belong[x]>belong[y])swap(q[qn].x,q[qn].y),swap(q[qn].bl,q[qn].br);
        }
        else d[i].x=x,d[i].val=y,d[i].ori=c[x],c[x]=y;
    }
    sort(q+1,q+qn+1);q[0].x=q[0].y=1,q[0].id=Q+1;
    for(int i=1;i<=qn;i++)
    {
        upt(q[i-1].id,q[i].id);
        solve(q[i-1].x,q[i].x),solve(q[i-1].y,q[i].y);
        int t=lca(q[i].x,q[i].y);
        rev(t),ans[q[i].id]=cur,rev(t);
    }
    for(int i=1;i<=Q;i++)
        if(!d[i].x)printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值