[BZOJ]3052 糖果公园 树上带修改莫队

7 篇文章 0 订阅

3052: [wc2013]糖果公园

Time Limit: 200 Sec   Memory Limit: 512 MB
Submit: 1264   Solved: 637
[ Submit][ Status][ Discuss]

Description

Input

Output

Sample Input

Sample Input

Sample Output

84
131
27
84

HINT


Source

[ Submit][ Status][ Discuss]


HOME Back

几句闲话

    这道题我很快的写完了但是调了超久,而且这道题200s的限时差点被别人喷成卡时狗...这道题如果T了的话要T三分钟多,想象就可怕,幸好我主要是WA,虽然T了一次快被机房的人喷成翔...狗眼观察法怎么都看不出来哪里错了,后来才发现,long long强转的时候后面被强转的东西不能打括号,不然的话就是先计算括号内的东西,爆了int以后再转long long就无济于事了...

题解

    一开始怎么都不相信是树上带修改莫队,毕竟n的三分之五次方的时间复杂度几乎就是暴力,这种复杂度只是为了卡掉暴力而已...然而题目的200s允许你这样暴力...一直没写过树上莫队,以及带修改莫队,正好这道题树上带修改莫队,一举两得,赶紧学习一波,一下学两个就当自虐了...
    我们可以发现通过括号序列(dfs序的感觉)可以讲两点的简单路径勾勒出来,因为出现两次的在对应括号序列区间我们会把它^掉,就好像()相消一样,一旦异或回0就消除当前点的贡献.将它转化为普通序列之后,就成为一道lca+普通序列带修改莫队了~注意块的大小.
#include<stdio.h>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=200005;
typedef long long dnt;
dnt sum,ans[maxn];
int num,cnt1,cnt2,idc,indexx,n,m,T,block,opt;
int f[maxn],g[maxn],in[maxn],seq[maxn*3],h[maxn],last[maxn],blo[maxn*2],id[maxn*2],cnt[maxn],c[maxn],v[maxn],w[maxn],st[maxn*2][19],vis[maxn];
inline int read(){
    register int x=0,f=1;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f*=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return f*x;
}
struct query{
    int l,r,t,id;
}q[maxn],b[maxn];
inline bool cmp(query x,query y){  
    return blo[x.l]<blo[y.l]||blo[x.l]==blo[y.l]&&blo[x.r]<blo[y.r]||blo[x.l]==blo[y.l]&&blo[x.r]==blo[y.r]&&x.t<y.t;  
}  
struct edge{
    int v,nxt;
}e[maxn*2]; 
inline void add(int u,int v){
    e[++num].v=v;
    e[num].nxt=h[u];
    h[u]=num;
}
void dfs(int u,int fa){
    f[u]=++idc,id[idc]=u;
    seq[++indexx]=u,in[u]=indexx;
    for(int i=h[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa) continue;
        dfs(v,u);
        seq[++indexx]=u;
    }
    g[u]=++idc,id[idc]=u;
}
inline void stha(){
    for(register int i=1;i<=indexx;i++) st[i][0]=in[seq[i]];
    for(int j=1;(1<<j)<indexx;j++)
     for(int i=1;i+(1<<j)-1<=indexx;i++)
      st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int query(int L,int R){
    if(L>R) swap(L,R);
    int len=R-L+1;
    int k=0,ans;
    while((1<<(k+1))<len) k++;
    ans=min(st[L][k],st[R-(1<<k)+1][k]);
    return seq[ans];
}
inline void update(int x){
    if(vis[x]) sum-=(dnt)v[c[x]]*w[cnt[c[x]]--];
    else sum+=(dnt)v[c[x]]*w[++cnt[c[x]]];
    vis[x]^=1;
}
inline void chg(int x,int y){
    if(vis[x]) update(x),c[x]=y,update(x);
    else c[x]=y;
}
int main(){
    register int i,l,r,t;
    n=read(),m=read(),T=read();
    for(i=1;i<=m;i++)  v[i]=read();
    for(i=1;i<=n;i++)  w[i]=read();
    for(i=1;i<n;i++)   l=read(),r=read(),add(l,r),add(r,l);
    for(i=1;i<=n;i++)  last[i]=c[i]=read();
    dfs(1,1);
    stha();
    block=(int)pow(n,2.0/3);
    for(i=1;i<=idc;i++)  blo[i]=(i-1)/block;
    for(i=1;i<=T;i++){
        opt=read(),l=read(),r=read();
        if(opt){
           if(f[l]>f[r]) swap(l,r);
           q[++cnt1].r=f[r],q[cnt1].t=cnt2,q[cnt1].id=cnt1;
           q[cnt1].l=(query(in[l],in[r])==l)?f[l]:g[l];
           continue;
        }
        b[++cnt2].l=l,b[cnt2].t=last[l],last[l]=b[cnt2].r=r;
    }
    l=1,r=0,t=1,sort(q+1,q+cnt1+1,cmp);
    for(i=1;i<=cnt1;i++){
        for(;t<=q[i].t;t++) chg(b[t].l,b[t].r);
        for(;t>q[i].t;t--)  chg(b[t].l,b[t].t);
        while(q[i].l<l) update(id[--l]);while(q[i].l>l) update(id[l++]);
        while(r>q[i].r) update(id[r--]);while(r<q[i].r) update(id[++r]);
        int x=id[l],y=id[r],lca=query(in[x],in[y]);
        if(x!=lca&&y!=lca) update(lca),ans[q[i].id]=sum,update(lca); 
        else ans[q[i].id]=sum;
    }
    for(i=1;i<=cnt1;i++) printf("%lld\n",ans[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值