bzoj3052 [wc2013]糖果公园 (树上带修改莫队)

bzoj3052 [wc2013]糖果公园

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=3052

题意:
(权限题贴题面)
这里写图片描述

这里写图片描述
这里写图片描述
Input
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2

Output
84
131
27
84

数据范围


这里写图片描述

题解:

关于树上莫队转序列莫队:
遍历一棵树记录in,out得到一个括号序:
例如:
这里写图片描述

得到:
1 2 3 3 2 4 10 8 8 5 5 10 4 6 7 7 9 9 6 1

对于两个点u,v的链: (in[u] < in[v])
1.u是v的祖先:从in[u]到in[v],中间出现两次的(in,out)的消掉,剩下的序列。
2.u不是v的祖先:从out[u]到in[v],中间出现两次的(in,out)的消掉,剩下的序列。
这个出现两次消掉就开个数序记录一下。

带修莫队见此

于是这道题就是裸题。


今天卡了一发评测,不仅第一次交没过(还是因为cmp写错TLE),还手滑连续交了两发TLE,还用的是别人的号233。
不远就听见Mercer在那骂:哪个sb交糖果公园还交两发。
以为没被认出来正在那庆幸,就被wys揭发了…


代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const int N=200005;
const int MXN=1000006;
const int P=17;
int n,m,q,col[N],st[N],cnt[N],V[N],W[N],head[N],to[2*N],nxt[2*N],num=0,blo;
int qs=0,md=0;
LL sum[N],ans[N],now=0,vis[2*N];
int dep[N],anc[N][P+3],in[N],out[N],seq[2*N],inc=0;
void build(int u,int v)
{
    num++;
    to[num]=v;
    nxt[num]=head[u];
    head[u]=num;
}
void dfs(int u,int f)
{
    inc++; seq[inc]=u; in[u]=inc;
    dep[u]=dep[f]+1;
    anc[u][0]=f;
    for(int i=1;i<P;i++) anc[u][i]=anc[anc[u][i-1]][i-1];

    for(int i=head[u];i;i=nxt[i])
    {   
        int v=to[i];
        if(v==f) continue;
        dfs(v,u);
    }
    inc++; seq[inc]=u; out[u]=inc;
}
struct Query
{
    int L,R,t,id,pos;
}Q[N];
struct Modify
{
    int pos,pre,nxt;
}M[N];
int getlca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;d;d>>=1,i++) if(d&1) u=anc[u][i];
    if(u==v) return u;
    for(int i=P-1;i>=0;i--)
    if(anc[u][i]!=anc[v][i]) {u=anc[u][i]; v=anc[v][i]; }
    return anc[u][0];
}
int getb(int x) {return (x-1)/blo+1;}
bool cmp(const Query &A,const Query &B)
{
    if(getb(A.L)!=getb(B.L)) return A.L<B.L;
    else if(getb(A.R)!=getb(B.R)) return A.R<B.R;
    else return A.t<B.t;
}
void modify(int pos,int opt)
{ 
    int u=seq[pos];  
    now-=1LL*sum[cnt[col[u]]]*1LL*V[col[u]];
    vis[u]^=1; if(vis[u]) cnt[col[u]]++; else cnt[col[u]]--;

    now+=1LL*sum[cnt[col[u]]]*1LL*V[col[u]];
}
void change(int tim,int opt)
{
    int pre=M[tim].pre; int nxt=M[tim].nxt; int pos=M[tim].pos;
    if(vis[pos])
    {
        now-=1LL*sum[cnt[col[pos]]]*1LL*V[col[pos]]; cnt[col[pos]]--;
        now+=1LL*sum[cnt[col[pos]]]*1LL*V[col[pos]]; 
        col[pos]= opt>0? nxt:pre;
        now-=1LL*sum[cnt[col[pos]]]*1LL*V[col[pos]]; cnt[col[pos]]++;
        now+=1LL*sum[cnt[col[pos]]]*1LL*V[col[pos]];
    }
    else col[pos]= opt>0? nxt:pre;  
}
void moto()
{
    int lf=1,rg=0,cur=0; 
    for(int i=1;i<=qs;i++)
    {
        while(rg<Q[i].R) modify(++rg,1);
        while(lf>Q[i].L) modify(--lf,1);
        while(rg>Q[i].R) modify(rg--,-1);
        while(lf<Q[i].L) modify(lf++,-1);
        while(cur<Q[i].t) change(++cur,1);
        while(cur>Q[i].t) change(cur--,-1);
        int lca=Q[i].pos; 
        ans[Q[i].id]=now;
        if(lca)
        {
            ans[Q[i].id]-=1LL*sum[cnt[col[lca]]]*1LL*V[col[lca]]; 
            ans[Q[i].id]+=1LL*sum[cnt[col[lca]]+1]*1LL*V[col[lca]]; 
        }
    }    
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=m;i++) scanf("%d",&V[i]);
    for(int i=1;i<=n;i++) scanf("%d",&W[i]);
    sum[0]=0; for(int i=1;i<=n;i++) sum[i]=sum[i-1]+1LL*W[i];
    for(int i=1;i<n;i++) 
    {
        int u,v; scanf("%d%d",&u,&v);
        build(u,v); build(v,u);
    }
    for(int i=1;i<=n;i++) {scanf("%d",&col[i]); st[i]=col[i];}
    dfs(1,1); blo=(int)pow((double)inc,(double)2/3);
    for(int i=1;i<=q;i++)
    {
        int opt,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==0)
        {
            md++; M[md].pre=col[x]; M[md].pos=x;col[x]=y; M[md].nxt=y;
        }
        else
        {
            qs++; Q[qs].id=qs; Q[qs].t=md; 
            int lca=getlca(x,y);  if(dep[x]>dep[y]) swap(x,y);
            if(lca!=x) Q[qs].pos=lca; else Q[qs].pos=0;
            if(in[x]>in[y]) swap(x,y);
            Q[qs].R=in[y]; Q[qs].L=(out[x]>in[y])? in[x]:out[x];    
        }   
    }
    for(int i=1;i<=n;i++) col[i]=st[i];
    sort(Q+1,Q+qs+1,cmp);
    moto();
    for(int i=1;i<=qs;i++)
    printf("%lld\n",ans[i]);
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值