BZOJ3052 & UOJ58:[WC2013]糖果公园——题解

http://uoj.ac/problem/58

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

——————————————————————————————————————

这题对于一个刚学莫队的人来说……挺萌的。

首先先对树分块,具体请看我的前一篇博客王室联邦我们就可以知道,假设我们要分块,我们设预期分块大小为s,则所有块的大小可为[s,3s]。

按照这种分块方法分块即可,注意为了我们算法的速度,分块大小s=n的2/3次方,证明可看小兔大佬的博客中单点修改莫队

(dfs同时预处理LCA所需要的几个数据,以后会用)

在那之后按照莫队的思路为询问排序,然后开始我们正式的算法。

(排序时0号点的l和r都设成1(看到下面的操作之后就会知道这样做干什么了))

这里说一下个别几个数组的含义。

1.sta[i]:i是否在当前路径上,是为1.

2.last[i]:第i个操作为修改时,修改前的颜色。

3.cc[i]:最终i点的颜色。

4.col[i]:当前操作时i点的颜色。

再说一个函数rev(i)表示将i这个点在我们的路径上被添加/删除。

剩下的应该都能看得懂,我就不说什么了。

首先按照排序后顺序扫询问,我们得到了我们当前的询问和前一个询问。

那么首先我们需要将前一个询问的id和后一个询问的id中间的修改操作修改了。

然后就是最神奇的操作了,solve操作的证明详见vfk的博客

(当然你也可以通过画图肉眼观察法证明,以下简述solve内部操作)

我们把当前的左询问节点l和之前的左询问节点l0之间的最短路取反。(当然同时对右节点也是一样)

然后取反左右询问节点的LCA,再更新cur,在取反LCA,这样我们就得到了这个询问的答案了。

#include<cstdio>
#include<stack>
#include<cctype>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100001;
const int INF=2147483647;
inline ll read(){
    ll X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int to;
    int nxt;
}edge[N*2];
struct qu{
    int id,l,r,bl,br;
}qry[N];
int n,m,Q,q,s,cnt,head[N];
int top,idx,stk[N],blk[N];
int anc[N][20],dep[N];
int v[N],cc[N],col[N],sum[N];
int a[N],b[N],op[N],last[N];
ll cur,ans[N],w[N];
bool sta[N];
inline void add(int u,int v){
    cnt++;
    edge[cnt].to=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
    return;
}
bool cmp(qu d,qu e){
    if(d.bl!=e.bl)return d.bl<e.bl;
    if(d.br!=e.br)return d.br<e.br;
    return d.id<e.id;
}
void dfs(int u){
    int st=top;
    dep[u]=dep[anc[u][0]]+1;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==anc[u][0])continue;
    anc[v][0]=u;
    dfs(v);
    if(top-st>=s){
        idx++;
        while(top>st)blk[stk[top--]]=idx;
    }
    }
    stk[++top]=u;
    return;
}
int LCA(int i,int j){
    if(dep[i]<dep[j])swap(i,j);
    for(int k=17;k>=0;k--){
        if(dep[anc[i][k]]>=dep[j])i=anc[i][k];
    }
    if(i==j)return i;
    for(int k=17;k>=0;k--){
        if(anc[i][k]!=anc[j][k])i=anc[i][k],j=anc[j][k];
    }
    return anc[i][0];
}
void init(){
    n=read();m=read();Q=read();s=pow(n,2.0/3.0);
    for(int i=1;i<=m;i++)v[i]=read();
    for(int i=1;i<=n;i++)w[i]=read()+w[i-1];
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)cc[i]=col[i]=read();
    dfs(1);
    while(top)blk[stk[top--]]=idx;
    for(int j=1;j<=17;j++){
        for(int i=1;i<=n;i++){
            anc[i][j]=anc[anc[i][j-1]][j-1];
        }
    }
    return;
}
inline void rev(int x){
    cur-=w[sum[col[x]]]*v[col[x]];
    sta[x]?sum[col[x]]--:sum[col[x]]++;
    sta[x]=!sta[x];
    cur+=w[sum[col[x]]]*v[col[x]];
    return;
}
inline void solve(int x,int y){
    int l=LCA(x,y);
    while(x!=l)rev(x),x=anc[x][0];
    while(y!=l)rev(y),y=anc[y][0];
    return;
}
inline void modify(int x,int y){
    if(!sta[x]){
    col[x]=y;
    return;
    }
    rev(x);
    col[x]=y;
    rev(x);
    return;
}
inline void upt(int tarT,int curT){
    while(curT<tarT){
    curT++;
    if(!op[curT])modify(a[curT],b[curT]);
    }
    while(curT>tarT){
    if(!op[curT])modify(a[curT],last[curT]);
    curT--;
    }
    return;
}
int main(){
    init();
    for(int i=1;i<=Q;i++){
    op[i]=read(),a[i]=read(),b[i]=read();
    if(op[i]){
        qry[++q].id=i;
        if(blk[a[i]]>blk[b[i]])swap(a[i],b[i]);
        qry[q].l=a[i];qry[q].r=b[i];
        qry[q].bl=blk[a[i]];qry[q].br=blk[b[i]];
    }else last[i]=cc[a[i]],cc[a[i]]=b[i];
    }
    sort(qry+1,qry+q+1,cmp);
    qry[0].l=qry[0].r=1;
    for(int i=1;i<=q;i++){
    upt(qry[i].id,qry[i-1].id);
    solve(qry[i].l,qry[i-1].l);solve(qry[i].r,qry[i-1].r);
    int l=LCA(qry[i].l,qry[i].r);
    rev(l);
    ans[qry[i].id]=cur;
    rev(l);
    }
    for(int i=1;i<=Q;i++){
    if(op[i])printf("%lld\n",ans[i]);
    }
    return 0;
}
posted @ 2018-01-03 20:25  luyouqi233  阅读(248)  评论(0编辑  收藏  举报