[bzoj3052][WC2013]糖果公园

题目大意

给定一颗n个结点的树。每个结点有一种颜色。颜色种类为m。
一条路径的价值定义为

i=1mw[cnt[i]]v[i]

其中cnt[i]表示这条路径上颜色i出现的次数。
现有q个操作,每个操作要么是询问一条路径的价值,要么是将一个结点的颜色种类修改。

带修改莫队

这是树上莫队,我们接下来只讨论序列莫队,树上莫队可以转化为序列莫队。
带修改莫队怎么做呀?
分块大小设为 n23
对于每个询问,用三元组表示(l,r,x)表示询问区间为[l,r]然后是在第x次修改操作后。
以左端点所在块为第一关键字,右端点所在块为第二关键字,已经经过的修改次数为第三关键字排序。
那么就和二维的莫队差不多,你只是需要额外兹瓷修改操作与撤销修改操作(其实都是修改操作)
可以证明总共的复杂度是 o(n53)
本题我的分块大小是取了1750。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
struct dong{
    int l,r,x,id,w;
    bool p;
};
int belong[maxn*2];
bool operator <(dong a,dong b){
    if (belong[a.l]<belong[b.l]) return 1;
    else if (belong[a.l]==belong[b.l]&&belong[a.r]<belong[b.r]) return 1;
    else if (belong[a.l]==belong[b.l]&&belong[a.r]==belong[b.r]&&a.x<b.x) return 1;
    else return 0;
}
dong ask[maxn];
int v[maxn],w[maxn],cnt[maxn],fi[maxn],la[maxn],a[maxn*2],c[maxn];
int h[maxn],go[maxn*2],next[maxn*2],b[maxn][3];
int f[maxn][25],d[maxn];
ll ans[maxn];
bool bz[maxn];
int i,j,k,l,r,x,t,n,m,q,block,tot,top,num,sum;
ll now;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void dfs(int x,int y){
    fi[x]=++top;
    a[top]=x;
    d[x]=d[y]+1;
    f[x][0]=y;
    int t=h[x];
    while (t){
        if (go[t]!=y) dfs(go[t],x);
        t=next[t];
    }
    la[x]=++top;
    a[top]=x;
}
int lca(int x,int y){
    int j;
    if (d[x]<d[y]) swap(x,y);
    if (d[x]!=d[y]){
        j=floor(log(d[x]-d[y])/log(2));
        while (j>=0){
            if (d[f[x][j]]>d[y]) x=f[x][j];
            j--;
        }
        x=f[x][0];
    }
    if (x==y) return x;
    j=floor(log(d[x])/log(2));
    while (j>=0){
        if (f[x][j]!=f[y][j]){
            x=f[x][j];
            y=f[y][j];
        }
        j--;
    }
    return f[x][0];
}
void change(int x){
    bz[x]^=1;
    if (bz[x]){
        cnt[c[x]]++;
        now+=(ll)w[cnt[c[x]]]*v[c[x]];
    }
    else{
        now-=(ll)w[cnt[c[x]]]*v[c[x]];
        cnt[c[x]]--;
    }
}
void in(int x){
    if (bz[b[x][2]]){
        now-=(ll)w[cnt[b[x][0]]]*v[b[x][0]];
        cnt[b[x][0]]--;
        cnt[b[x][1]]++;
        now+=(ll)w[cnt[b[x][1]]]*v[b[x][1]];

    }
    c[b[x][2]]=b[x][1];
}
void out(int x){
    if (bz[b[x][2]]){
        now-=(ll)w[cnt[b[x][1]]]*v[b[x][1]];
        cnt[b[x][1]]--;
        cnt[b[x][0]]++;
        now+=(ll)w[cnt[b[x][0]]]*v[b[x][0]];

    }
    c[b[x][2]]=b[x][0];
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,m) scanf("%d",&v[i]);
    fo(i,1,n) scanf("%d",&w[i]);
    fo(i,1,n-1){
        scanf("%d%d",&j,&k);
        add(j,k);add(k,j);
    }
    dfs(1,0);
    fo(j,1,floor(log(n)/log(2)))
        fo(i,1,n)
            f[i][j]=f[f[i][j-1]][j-1];
    fo(i,1,n) scanf("%d",&c[i]);
    fo(i,1,q){
        scanf("%d",&t);
        if (!t){
            scanf("%d%d",&j,&k);
            b[++num][0]=c[j];
            b[num][1]=c[j]=k;
            b[num][2]=j;
        }
        else{
            scanf("%d%d",&j,&k);
            t=lca(j,k);
            if (t==j||t==k){
                ask[++sum].p=0;
                l=fi[j];r=fi[k];
                if (l>r) swap(l,r);
            }
            else{
                ask[++sum].p=1;
                ask[sum].w=t;
                if (fi[j]>fi[k]) swap(j,k);
                l=la[j];r=fi[k];
            }
            ask[sum].l=l;ask[sum].r=r;
            ask[sum].id=sum;
            ask[sum].x=num;
        }
    }
    fd(i,num,1) c[b[i][2]]=b[i][0];
    block=1750;
    fo(i,1,2*n) belong[i]=(i-1)/block+1;
    sort(ask+1,ask+sum+1);
    l=r=1;x=0;
    change(a[1]);
    fo(i,1,sum){
        while (x>ask[i].x){
            out(x);
            x--;
        }
        while (x<ask[i].x){
            x++;
            in(x);
        }
        while (l>ask[i].l){
            l--;
            change(a[l]);
        }
        while (l<ask[i].l){
            change(a[l]);
            l++;
        }
        while (r<ask[i].r){
            r++;
            change(a[r]);
        }
        while (r>ask[i].r){
            change(a[r]);
            r--;
        }
        if (ask[i].p) change(ask[i].w);
        ans[ask[i].id]=now;
        if (ask[i].p) change(ask[i].w);
    }
    fo(i,1,sum) printf("%lld\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值