题目链接

Solution [WC2013]糖果公园

题目大意:多次询问树上路径权值(计算方法题目已给出),带修改可离线

分析:这题可谓集各种莫队算法于一身()

首先,观察计算路径权值

\(\sum_cval[c] \sum_{i =1}^{cnt[c]}w[c]\)

\(val\)表示美味指数,\(cnt\)表示出现次数,\(w\)表示新鲜指数

显然无论增加还是减少点,对答案的影响都是可以\(O(1)\)求的

然后树上莫队就好了

既然是莫队,本质上还是一个暴力,朴素莫队对序列分块,这里对树进行分块

[SCOI2005]王室联邦这题就是给树分块的板子题

然后套用莫队思想,只不过和序列上的莫队有点区别

朴素莫队可以直接在序列上跳\(l\),\(r\)指针,这里跳的方式有点不同

设当前的记录\(u,v\)这条路径上的答案,我们要求\(tu,tv\)路径上的答案,假如直接消除\(u,v\)再加入\(tu,tv\)就和暴力没有任何区别

我们可以翻转\(u,tu\)路径和\(v,tv\)路径,也就是把出现过的删掉,没出现过的加进去(\(LCA\)这玩意儿比较烦,单独处理),具体证明可以看这位巨佬的\(blog\)

待修莫队记个时间戳就好,块大小\(n^{\frac{2}{3}}\)

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <vector>
#include <stack>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100,maxdep = 25;
inline int read(){
    int x = 0;char c = getchar();
    while(!isdigit(c))c = getchar();
    while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    return x;
}
vector<int> G[maxn];
stack<int> stk;
inline void addedge(int from,int to){G[from].push_back(to);};
int dep[maxn],faz[maxn][maxdep + 1],col[maxn],v[maxn],w[maxn],vis[maxn],cnt[maxn],val[maxn],n,m,q,siz,col_tot,ques_tot,nowu,nowv,nowtim;
ll ans[maxn],tmpans;
inline void dfs(int u){
    int tmp = stk.size();
    dep[1] = 1;
    for(int i = 1;i <= maxdep;i++)
        faz[u][i] = faz[faz[u][i - 1]][i - 1];
    for(int v : G[u]){
        if(v == faz[u][0])continue;
        dep[v] = dep[u] + 1;
        faz[v][0] = u;
        dfs(v);
        if(stk.size() - tmp >= siz){
            col_tot++;
            while(stk.size() != tmp)col[stk.top()] = col_tot,stk.pop();
        }
    }
    stk.push(u);
}
inline int lca(int x,int y){
    if(dep[x] < dep[y])swap(x,y);
    for(int i = maxdep;i >= 0;i--)
        if(dep[faz[x][i]] >= dep[y])x = faz[x][i];
    if(x == y)return x;
    for(int i = maxdep;i >= 0;i--)
        if(faz[x][i] != faz[y][i])
            x = faz[x][i],y = faz[y][i];
    return faz[x][0];
}
inline void rev(int u){//翻转点
    if(vis[u]) tmpans -= (ll)v[val[u]] * w[cnt[val[u]]--];
    else tmpans += (ll)v[val[u]] * w[++cnt[val[u]]];
    vis[u] ^= 1;
}
inline void modify(int x,int y){//翻转路径
    if(dep[x] < dep[y])swap(x,y);
    while(dep[faz[x][0]] >= dep[y])
        rev(x),x = faz[x][0];
    while(x != y)
        rev(x),rev(y),x = faz[x][0],y = faz[y][0];
}
struct Modi{
    int pos,from,to;
}modi[maxn];
inline void add_modi(int tim){
    Modi tmp = modi[tim];bool flag = false;
    if(vis[tmp.pos])flag = true,rev(tmp.pos);
    val[tmp.pos] = tmp.to;
    if(flag)rev(tmp.pos);
}
inline void del_modi(int tim){
    Modi tmp = modi[tim];bool flag = false;
    if(vis[tmp.pos])flag = true,rev(tmp.pos);
    val[tmp.pos] = tmp.from;
    if(flag)rev(tmp.pos);
}
struct Ques{
    int u,v,tim,id;
    bool operator < (const Ques &rhs)const{
        if(col[u] != col[rhs.u])return col[u] < col[rhs.u];
        if(col[v] != col[rhs.v])return col[v] < col[rhs.v];
        return tim < rhs.tim;
    }
}ques[maxn];
int main(){
    n = read(),m = read(),q = read();
    siz = pow(n,0.66);
    for(int i = 1;i <= m;i++)v[i] = read();
    for(int i = 1;i <= n;i++)w[i] = read();
    for(int u,v,i = 1;i < n;i++)
        u = read(),v = read(),addedge(u,v),addedge(v,u);
    dfs(1);
    for(int i = 1;i <= n;i++)
        val[i] = read();
    for(int opt,x,y,i = 1;i <= q;i++){
        opt = read(),x = read(),y = read();
        if(!opt)modi[++nowtim] = Modi{x,val[x],y},val[x] = y;
        else ques_tot++,ques[ques_tot] = Ques{x,y,nowtim,ques_tot};
    }
    sort(ques + 1,ques + 1 + ques_tot);
    nowu = nowv = 1;
    for(int i = 1;i <= ques_tot;i++){
        while(nowtim < ques[i].tim)add_modi(++nowtim);
        while(nowtim > ques[i].tim)del_modi(nowtim--);
        modify(nowu,ques[i].u);
        modify(nowv,ques[i].v);
        nowu = ques[i].u,nowv = ques[i].v;
        int lc = lca(ques[i].u,ques[i].v);//LCA单独处理
        rev(lc);
        ans[ques[i].id] = tmpans;
        rev(lc);
    }
    for(int i = 1;i <= ques_tot;i++)printf("%lld\n",ans[i]);
    return 0;
}
01-22 03:31