带修改的树上莫队 WC糖果公园

今天跟着TYB来了一发带修改的树上莫队
自然这是裸题啦
然后呢,由于是第一次打,于是为了学习,不要脸地抄代码。。
我是看这里的啦http://www.cnblogs.com/ljh2000-jump/p/6241541.html
建议有带修改莫队基础和树分块基础的同学直接膜代码就好了。。
不是特别难的啦,可以看懂。。
然后复杂度那里其实我看的不是特别懂。。
高仿CODE(有少量注释):

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=100005;
int n,m,q;
int block;
int val[N],w[N];
int c[N],pre[N];
struct ask{int l,r,t,lb,rb,id;}a[N];
struct UP{int x,y,pre;}b[N];
int cnt2=0,cnt1=0;
struct qq{int x,y,last;}s[N*2];int num,last[N];
void init (int x,int y)
{
    num++;
    s[num].x=x;s[num].y=y;
    s[num].last=last[x];
    last[x]=num;
}
int dep[N],cc=0;
int f[N][20];
int sta[N],top=0;
int belong[N],ecnt=0;
bool vis[N];//是否在莫队里面 
int dfs (int x,int fa)
{
    int remain=0;
    for (int u=last[x];u!=-1;u=s[u].last)
    {
        int y=s[u].y;
        if (y==fa) continue;
        f[y][0]=x;dep[y]=dep[x]+1;
        remain+=dfs(y,x);
        if (remain>=block)
        {
            ecnt++;
            while (remain>0) belong[sta[top--]]=ecnt,remain--;
        }
    }
    sta[++top]=x;
    return remain+1;
}
bool cmp(ask q,ask qq)
{
    if(q.lb==qq.lb && q.rb==qq.rb) return q.t<qq.t;
    if(q.lb==qq.lb) return q.rb<qq.rb;
    return q.lb<qq.lb;
}
LL ans=0;
int Tim[N];//这个颜色出现了多少次 
void update (int x)//改变这个点的状态 
{
    if (vis[x])//这个在莫队里面
    {
        vis[x]=false;
        ans=ans-(LL)w[Tim[c[x]]]*val[c[x]];
        Tim[c[x]]--;
    }
    else
    {
        vis[x]=true;
        Tim[c[x]]++;
        ans=ans+(LL)w[Tim[c[x]]]*val[c[x]];
    }
}
void lalal (int x,int C)//将x修改为C
{
    if (!vis[x]) c[x]=C;
    else
    {
        update(x);
        c[x]=C;
        update(x);
    }
}
void change (int x,int y)//加上这个路径   但是LCA还不在 
{
    while (x!=y)
    {
        //printf("%d %d\n",x,y);
        if (dep[x]<dep[y]) update(y),y=f[y][0];
        else update(x),x=f[x][0];
    }
}
LL A[N];
int lca (int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    for (int u=18;u>=0;u--) if (dep[f[y][u]]>=dep[x]) y=f[y][u];
    if (x==y) return x;
    for (int u=18;u>=0;u--)
        if (f[y][u]!=f[x][u])
            y=f[y][u],x=f[x][u];    
    return f[x][0];
}
void solve ()
{
    sort(a+1,a+1+cnt1,cmp);cnt2=a[1].t;
    memset(vis,false,sizeof(vis));
    memset(Tim,0,sizeof(Tim));
    for (int u=1;u<=a[1].t;u++) lalal(b[u].x,b[u].y);
    //for (int u=1;u<=n;u++) printf("%d ",c[u]);
    change(a[1].l,a[1].r);

    int LCA=lca(a[1].l,a[1].r);update(LCA);//可以知道,我们这条链还少了LCA
    A[a[1].id]=ans;
    //printf("ans:%lld %d\n",ans,LCA);
    update(LCA);//这里的目的是为了下一次的跳,可以知道,当我们更改的是后,l,r的路径要是空,否则会有多算
    for (int u=2;u<=cnt1;u++)
    {
        while (cnt2<a[u].t) cnt2++,lalal(b[cnt2].x,b[cnt2].y);
        while (cnt2>a[u].t) lalal(b[cnt2].x,b[cnt2].pre),cnt2--;
        change(a[u].l,a[u-1].l);change(a[u].r,a[u-1].r);
        LCA=lca(a[u].l,a[u].r);
        update(LCA);
        A[a[u].id]=ans;
        update(LCA);
    }
}
void ins ()
{
    num=0;memset(last,-1,sizeof(last));
    scanf("%d%d%d",&n,&m,&q);
    block=(int)pow(n,0.60);
    for (int u=1;u<=m;u++) scanf("%d",&val[u]);
    for (int u=1;u<=n;u++) scanf("%d",&w[u]);
    for (int u=1;u<n;u++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        init(x,y);init(y,x);
    }
    for (int u=1;u<=n;u++)  {scanf("%d",&c[u]);pre[u]=c[u];}
    ecnt=0;dep[1]=1;dfs(1,0);
    for (int u=1;u<=17;u++)
        for (int i=1;i<=n;i++)
            f[i][u]=f[f[i][u-1]][u-1];
    for (int u=1;u<=q;u++)
    {
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if (op==0) {b[++cnt2].pre=pre[x];b[cnt2].x=x;b[cnt2].y=y;pre[x]=y;}
        else
        {
            a[++cnt1].l=x;a[cnt1].r=y;
            a[cnt1].lb=belong[x];a[cnt1].rb=belong[y];
            a[cnt1].t=cnt2;a[cnt1].id=cnt1;
        }
    }
}
int main()
{
    ins();
    solve();
    for (int u=1;u<=cnt1;u++) printf("%lld\n",A[u]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值