forezxl的博客

ZJ菜鸡OIer

洛谷P4074 [WC2013]糖果公园(BZOJ3052)

树上莫队

洛谷题目传送门
BZOJ题目传送门

学了发树上莫队。

树上分块是按照王室联邦这道题来分的。

如果没有修改的话直接在树上跳就好了。现在有了修改操作,那么先和带修改莫队一样记一个t,每次询问前先还原到当前时间。

接下来和序列操作不一样的是移动两个端点。做法是开一个f[i]表示i是否在当前处理的路径上,当从(x1,y1)转移到(x2,y2)时,把(x1,x2)(y1,y2)路径上的点的f取反并更新答案(不包括LCA)。

这么做为什么是对的呢?

S(v,u)代表 vu的路径上的结点的集合。用root来代表根结点,用lca(v,u)来代表v,u的最近公共祖先。那么S(v,u)=S(root,v) xor S(root,u) xor lca(v,u)。其中xor是集合的对称差,简单来说就是节点出现两次消掉。

lca很讨厌,于是再定义T(v,u)=S(root,v) xor S(root,u)

观察将curV移动到targetV前后T(curV,curU)变化:

T(curV,curU)=S(root,curV) xor S(root,curU)

T(targetV,curU)=S(root,targetV) xor S(root,curU)

取对称差:

T(curV,curU) xor T(targetV,curU)=(S(root,curV) xor S(root,curU)) xor (S(root,targetV) xor S(root,curU))

由于对称差的交换律、结合律:

T(curV,curU) xor T(targetV,curU)=S(root,curV) xor S(root,targetV)

两边同时xor T(curV,curU)

T(targetV,curU)=T(curV,curU) xor S(root,curV) xor S(root,targetV)

发现最后两项很爽……哇哈哈

T(targetV,curU)=T(curV,curU) xor T(curV,targetV)

也就是说,更新的时候,xor T(curV,targetV)就行了。

即,对curVtargetV路径(除开lca(curV,targetV))上的结点,将它们的存在性取反即可。

—By vfleaking

代码:

#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
typedef long long LL;
struct edge{ int next,to; }ed[N<<1];
struct que{ int x,y,id,t; }q[N];
struct mod{ int x,y,nxt; }a[N];
int n,m,s,k,n1,n2,b,tp,p,nd,h[N],dep[N],fa[N][20];
int ha[N],stk[N],num[N],t[N],v[N],w[N],ti[N],c[N];
LL sum,ans[N]; bool f[N];
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
F int _read(){
    int x=0,f=1; char ch=readc();
    while (!isdigit(ch)) { if (ch=='-') f=-1; ch=readc(); }
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x*f;
}
F void writec(LL x){ if (x>9) writec(x/10); putchar(x%10+48); }
F void _write(LL x){ writec(x),putchar('\n'); }
#define addedge(x,y) ed[++k]=(edge){h[x],y},h[x]=k
F bool cmp(que x,que y){
    bool f1=num[x.x]==num[y.x],f2=num[x.y]==num[y.y];
    return num[x.x]<num[y.x]||(f1&&num[x.y]<num[y.y])||(f1&&f2&&x.t<y.t);
}
void dfs(int x){
    ti[x]=++p; int now=tp;
    for (int i=h[x],v;i;i=ed[i].next)
        if ((v=ed[i].to)!=fa[x][0]){
            fa[v][0]=x,dep[v]=dep[x]+1,dfs(v);
            if (tp-now>=b) for (nd++;tp!=now;) num[stk[tp--]]=nd; 
        }
    stk[++tp]=x;
}
F void Make(){
    for (int j=1;j<20;j++)
        for (int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
}
F int LCA(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    for (int j=19;~j;j--)
        if (dep[fa[x][j]]>=dep[y]) x=fa[x][j];
    if (x==y) return x;
    for (int j=19;~j;j--)
        if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
F void rvrs(int x){
    if (!f[x]) f[x]=true,t[c[x]]++,sum+=1ll*w[t[c[x]]]*v[c[x]];
    else f[x]=false,sum-=1ll*w[t[c[x]]]*v[c[x]],t[c[x]]--;
}
F void mdfy(int x,int y){
    if (!f[x]) c[x]=y; else rvrs(x),c[x]=y,rvrs(x);
}
F void srch(int x,int y){
    while (x!=y)
        if (dep[x]<dep[y]) rvrs(y),y=fa[y][0];
        else rvrs(x),x=fa[x][0];
}
int main(){
    n=_read(),m=_read(),s=_read();
    for (int i=1;i<=m;i++) v[i]=_read();
    for (int i=1;i<=n;i++) w[i]=_read();
    for (int i=1,x,y;i<n;i++)
        x=_read(),y=_read(),addedge(x,y),addedge(y,x);
    for (int i=1;i<=n;i++) ha[i]=c[i]=_read();
    dep[1]=1,b=(int)pow(n,0.6),dfs(1),Make();
    for (int i=1;i<=s;i++){
        int f=_read(),x=_read(),y=_read();
        if (!f) a[++n2]=(mod){x,y,ha[x]},ha[x]=y;
        else{
            if (ti[x]>ti[y]) swap(x,y);
            q[++n1]=(que){x,y,n1,n2};
        }
    }
    sort(q+1,q+n1+1,cmp);
    for (int i=1;i<=q[1].t;i++) mdfy(a[i].x,a[i].y);
    srch(q[1].x,q[1].y); int lca=LCA(q[1].x,q[1].y);
    rvrs(lca),ans[q[1].id]=sum,rvrs(lca);
    for (int i=2,j;i<=n1;i++){
        for (j=q[i-1].t+1;j<=q[i].t;j++) mdfy(a[j].x,a[j].y);
        for (j=q[i-1].t;j>q[i].t;j--) mdfy(a[j].x,a[j].nxt);
        srch(q[i-1].x,q[i].x),srch(q[i-1].y,q[i].y);
        int l=LCA(q[i].x,q[i].y);
        rvrs(l),ans[q[i].id]=sum,rvrs(l);
    }
    for (int i=1;i<=n1;i++) _write(ans[i]);
    return 0;
}
阅读更多
版权声明:转载请注明出处 https://blog.csdn.net/a1799342217/article/details/80356802
文章标签: 洛谷 BZOJ 树上莫队
所属专栏: 蒟蒻zxl的Blog专栏
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

洛谷P4074 [WC2013]糖果公园(BZOJ3052)

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭