WC2013糖果公园

Candyland 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩。

糖果公园的结构十分奇特,它由 nn 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 11 至 nn。有 n−1n−1 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。

糖果公园所发放的糖果种类非常丰富,总共 mm 种,它们的编号依次为 11 至 mm。每一个糖果发放处都只发放某种特定的糖果,我们用 cici 来表示 ii 号游览点的糖果。

来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。

大家对不同类型的糖果的喜爱程度都不尽相同。根据游客们的反馈打分,我们得到了糖果的美味指数,第 ii 种糖果的美味指数为 vivi。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 ii 次品尝某类糖果的新奇指数 wiwi,如果一位游客第 ii 次品尝第 jj 种糖果,那么他的愉悦指数 HH 将会增加对应的美味指数与新奇指数的乘积,即 vjwivjwi。这位游客游览公园的愉悦指数最终将是这些乘积的和。

当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 mm 种中的一种),这样的目的是能够让游客们总是感受到惊喜。

糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。

输入格式
第一行包含三个正整数 n,m,qn,m,q,分别表示游览点个数、糖果种类数和操作次数。

第二行包含 mm 个正整数 v1,v2,…,vmv1,v2,…,vm。

第三行包含 nn 个正整数 w1,w2,…,wnw1,w2,…,wn。

第四行到第 n+2n+2 行,每行包含两个正整数 ai,biai,bi,表示这两个游览点之间有路径可以直接到达。

第 n+3n+3 行包含 nn 个正整数 c1,c2,…,cnc1,c2,…,cn。

接下来 qq 行,每行包含三个整数 t,x,yt,x,y,表示一次操作:

若 tt 为 00,则 1≤x≤n1≤x≤n,1≤y≤m1≤y≤m,表示编号为 xx 的游览点发放的糖果类型改为 yy;

若 tt 为 11,则 1≤x,y≤n1≤x,y≤n,表示对出发点为 xx,终止点为 yy 的路线询问愉悦指数。

输出格式
按照输入的先后顺序,对于每个 tt 为 11 的操作输出一行,用一个正整数表示答案。

正解:树上带修改的莫队

首先dfs用类似BZOJ1086 王室联邦的方法对树进行分块,之后就是经典的树上带修改的莫队了。

与序列上的莫队不同的是,我们从上一次查询转移到下一次查询的方法。用类似亦或的思想(被同一个数亦或两次就相当于没有操作),于是我们可以对q[i-1].l到q[i].l以及q[i-1].r到q[i].r分别进行change操作,lca要特殊处理。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=200010;
int size;
vector<int>g[maxn];
int c[maxn],v[maxn],w[maxn];
int fa[maxn][20],dep[maxn];
struct Query{
    int l,r,t,id;
    int bx,by;
    bool operator <(const Query& A)const{
        return bx<A.bx || (bx==A.bx && (by<A.by || (by==A.by && t<A.t)));
    }
}q[maxn];
struct node{
    int x,y,last;
}r[maxn];
int last[maxn],vis[maxn];
int n,m,cntr,cntq,Q;
int be[maxn];
void read(int& x){
    x=0; char c=getchar();
    while(c<'0' || c>'9') c=getchar();
    while(c>='0' && c<='9'){ x=x*10+c-'0'; c=getchar(); }
}
int top,cnt,tot;
int st[maxn],dfn[maxn];
void dfs(int x,int f){
    dfn[x]=++tot;
    int bottom=top;
    for(int i=0;i<g[x].size();i++){
        int v=g[x][i];
        if(v==f) continue;
        dep[v]=dep[x]+1;fa[v][0]=x;
        dfs(v,x);
        if(top-bottom>=size){
            ++cnt;
            while(top!=bottom) be[st[top--]]=cnt;
        }
    }
    st[++top]=x;
}
int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    int d=dep[x]-dep[y];
    for(int i=18;i>=0;i--) if(d & (1<<i)) x=fa[x][i];
    if(x==y) return x;
    for(int i=18;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int time[maxn];
LL ans,Ans[maxn];
void update(int x){
    if(!vis[x]){
        vis[x]=1;
        ans+=(LL)v[c[x]]*w[++time[c[x]]];
    }else{
        vis[x]=0;
        ans-=(LL)v[c[x]]*w[time[c[x]]--];
    }
}
void modify(int x,int col){
    if(!vis[x]) c[x]=col;
    else{
        update(x);
        c[x]=col;
        update(x);
    }
}
void change(int x,int y){
    while(x!=y){
        if(dep[x]>dep[y]) update(x),x=fa[x][0];
        else update(y),y=fa[y][0];
    }
}
void solve(){
    int head=q[1].t;
    for(int i=1;i<=head;i++) modify(r[i].x,r[i].y);
    change(q[1].l,q[1].r);
    int LCA=lca(q[1].l,q[1].r);
    update(LCA);
    Ans[q[1].id]=ans;
    update(LCA);
    for(int i=2;i<=cntq;i++){
        while(head<q[i].t) head++,modify(r[head].x,r[head].y);
        while(head>q[i].t) modify(r[head].x,r[head].last),head--;
        change(q[i-1].l,q[i].l);
        change(q[i-1].r,q[i].r);
        LCA=lca(q[i].l,q[i].r);
        update(LCA);
        Ans[q[i].id]=ans;
        update(LCA);
    }
}
int main(){
    read(n);read(m);read(Q);
    size=(int)pow(n,0.60);
    for(int i=1;i<=m;i++) read(v[i]); for(int i=1;i<=n;i++) read(w[i]);
    int u,v;
    for(int i=1;i<n;i++){
        read(u);read(v);
        g[u].push_back(v);g[v].push_back(u);
    }
    for(int i=1;i<=n;i++) read(c[i]),last[i]=c[i];
    dep[1]=1; dfs(1,0); for(int j=1;j<=18;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
    while(top) be[st[top--]]=cnt;
    while(Q--){
        int op,x,y;
        read(op);read(x);read(y);
        if(!op){
            r[++cntr].last=last[x],r[cntr].x=x,r[cntr].y=y,last[x]=y;
        }else{
            if(dfn[x]>dfn[y]) swap(x,y);
            q[++cntq].l=x,q[cntq].r=y,q[cntq].t=cntr,q[cntq].id=cntq,q[cntq].bx=be[x],q[cntq].by=be[y];
        }
    }
    sort(q+1,q+1+cntq);
    solve();
    for(int i=1;i<=cntq;i++) printf("%lld\n",Ans[i]);
    return 0;
}

^_^

调一下代码(WC2013糖果公园,1<=n,m,q<=1e5),RE了: #include<bits/stdc++.h> using namespace std; long long n, m, Q, block, top, v[200010], w[200010], cn[200010]; vector<long long> vec[200010]; long long ans, cnt1, cnt2, pt, pl, pr; long long stk[200010], t1[200010], t2[200010]; long long dep[200010], q[200010], fa[26][200010]; long long sum[200010], cnt[200010], st[200010], pp[200010]; struct node{ long long l, r, pp; long long time, qtime; friend bool operator<(node p, node q){ return (p.l / block != q.l / block) ? (p.l < q.l) : ((p.r / block != q.r / block ? (p.r < q.r) : (p.time < q.time))); } }b1[200010]; struct node2{ long long l, r, time; }b2[200010]; void dfs(long long x, long long fa){ stk[++ top] = x; t1[x] = top; for(auto i : vec[x]){ if(i == fa) continue; dfs(i, x); } stk[++ top] = x; t2[x] = top; } void bfs(long long rt){ memset(dep, 0x3f, sizeof(dep)); dep[0] = 0, dep[rt] = 1; long long hh = 0, tt = 0; q[0] = rt; while(hh <= tt){ long long t = q[hh ++]; for(auto i : vec[t]){ if(dep[i] > dep[t] + 1){ dep[i] = dep[t] + 1; q[++ tt] = i; fa[i][0] = t; for(long long k = 1; k <= 25; ++ k){ fa[i][k] = fa[fa[i][k - 1]][k - 1]; } } } } } long long lca(long long x, long long y){ if(dep[x] < dep[y]) swap(x, y); for(long long i = 25; i >= 0; -- i){ if(dep[fa[x][i]] >= dep[y]){ x = fa[x][i]; } } if(x == y) return x; for(long long i = 25; i >= 0; -- i){ if(fa[x][i] != fa[y][i]){ x = fa[x][i], y = fa[y][i]; } } return fa[x][0]; } void add(long long x){ st[x] ^= 1; if(st[x]){ cnt[cn[x]] ++; ans += v[cn[x]] * w[cnt[cn[x]]]; } else{ ans -= v[cn[x]] * w[cnt[cn[x]]]; cnt[cn[x]] --; } } int main(){ scanf("%lld %lld %lld", &n, &m, &Q); for(long long i = 1; i <= m; ++ i){ scanf("%lld", &v[i]); } for(long long i = 1; i <= n; ++ i){ scanf("%lld", &w[i]); } for(long long i = 1; i < n; ++ i){ long long x, y; scanf("%lld %lld", &x, &y); vec[x].push_back(y); vec[y].push_back(x); } for(long long i = 1; i <= n; ++ i){ scanf("%lld", &cn[i]); } dfs(1, -1); bfs(1); for(long long i = 1; i <= Q; ++ i){ long long op, l, r; scanf("%lld %lld %lld", &op, &l, &r); if(op == 0){ b2[++ cnt2].l = l; b2[cnt2].r = r; } else{ if(t1[l] > t1[r]) swap(l, r); long long p = lca(l, r); if(p == l){ b1[++ cnt1].l = t1[l]; b1[cnt1].r = t1[r]; b1[cnt1].pp = 0; } else{ b1[++ cnt1].l = t2[l]; b1[cnt1].r = t1[r]; b1[cnt1].pp = p; } b1[cnt1].time = cnt1; b1[cnt1].qtime = cnt2; } } block = max(1ll, (long long)cbrt(top * top)); sort(b1 + 1, b1 + cnt1 + 1); pt = 0, pl = 1, pr = 0; for(long long i = 1; i <= cnt1; ++ i){ while(pr < b1[i].r) ++ pr, add(stk[pr]); while(pr > b1[i].r) add(stk[pr]), -- pr; while(pl > b1[i].l) -- pl, add(stk[pl]); while(pl < b1[i].l) add(stk[pl]), ++ pl; while(pt < b1[i].qtime){ ++ pt; if(st[b2[pt].l]){ add(b2[pt].l); swap(b2[pt].r, cn[b2[pt].l]); add(b2[pt].l); } else{ swap(b2[pt].r, cn[b2[pt].l]); } } while(pt > b1[i].qtime){ if(st[b2[pt].l]){ add(b2[pt].l); swap(b2[pt].r, cn[b2[pt].l]); add(b2[pt].l); } else{ swap(b2[pt].r, cn[b2[pt].l]); } -- pt; } if(b1[i].pp){ add(b1[i].pp); } pp[b1[i].time] = ans; if(b1[i].pp){ add(b1[i].pp); } } for(long long i = 1; i <= cnt1; ++ i){ printf("%lld\n", pp[i]); } return 0; }
03-08
# P4074 [WC2013] 糖果公园 ## 题目描述 Candyland 有一座糖果公园公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩。 糖果公园的结构十分奇特,它由 $n$ 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 $1$ 至 $n$。有 $n - 1$ 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。 糖果公园所发放的糖果种类非常丰富,总共有 $m$ 种,它们的编号依次为 $1$ 至 $m$。每一个糖果发放处都只发放某种特定的糖果,我们用 $C_i$ 来表示 $i$ 号游览点的糖果。 来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。 大家对不同类型糖果的喜爱程度都不尽相同。 根据游客们的反馈打分,我们得到了糖果的美味指数, 第 $i$ 种糖果的美味指数为 $V_i$。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 $i$ 次品尝某类糖果的新奇指数 $W_i$。如果一位游客第 $i$ 次品尝第 $j$ 种糖果,那么他的愉悦指数 $H$ 将会增加对应的美味指数与新奇指数的乘积,即 $V_j \times W_i$。这位游客游览公园的愉悦指数最终将是这些乘积的和。 当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 $m$ 种中的一种),这样的目的是能够让游客们总是感受到惊喜。 糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。 ## 输入格式 第一行包含三个正整数 $n, m, q$, 分别表示游览点个数、 糖果种类数和操作次数。 第二行包含 $m$ 个正整数 $V_1, V_2, \ldots, V_m$。 第三行包含 $n$ 个正整数 $W_1, W_2, \ldots, W_n$。 第四行到第 $n + 2$ 行,每行包含两个正整数 $A_i, B_i$,表示这两个游览点之间有路径可以直接到达。 第 $n + 3$ 行包含 $n$ 个正整数 $C_1, C_2, \ldots, C_n$。 接下来 $q$ 行, 每行包含三个整数 $Type, x, y$,表示一次操作: - 若 $Type$ 为 $0$,则 $1 \leq x \leq n$, $1 \leq y \leq m$,表示将编号为 $x$ 的游览点发放的糖果类型改为 $y$; - 若 $Type$ 为 $1$,则 $1 \leq x, y \leq n$,表示对出发点为 $x$,终止点为 $y$ 的路线询问愉悦指数。 ## 输出格式 按照输入的先后顺序,对于每个 $Type$ 为 $1$ 的操作输出一行,用一个正整数表示答案。 ## 输入输出样例 #1 ### 输入 #1 ``` 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 ``` ### 输出 #1 ``` 84 131 27 84 ``` ## 说明/提示 【样例解释】 我们分别用 ![](https://cdn.luogu.com.cn/upload/image_hosting/isw3ib3u.png) 代表 $C_i$ 为 $1$、 $2$、 $3$ 的节点,在修改之前: ![](https://cdn.luogu.com.cn/upload/image_hosting/ttkzii1u.png) 在将 $C_2$ 修改为 $1$ 之后: ![](https://cdn.luogu.com.cn/upload/image_hosting/izro364w.png) 【数据规模与约定】 对于所有的数据: $1 \leq V_i, W_i \leq 10^6$,$1 \leq A_i, B_i \leq n$, $1 \leq C_i \leq m$, $W_1, W_2, \ldots, W_n$ 是非递增序列,即对任意 $1 < i \leq n$, 满足 $W_i \le W_{i-1}$。 其它的限制条件如下表所示: ![QQ20180113072014.png](https://cdn.luogu.com.cn/upload/image_hosting/g6884nx1.png) 为什么过不了样例 ```cpp #include <iostream> #include <algorithm> #include <vector> #include <cmath> using namespace std; const int BITS = 20; const int MAX_N = 200050; int n, m, q, v[MAX_N], w[MAX_N], c[MAX_N]; int in[MAX_N], out[MAX_N], dfn[MAX_N], tim; int fa[MAX_N][BITS], dep[MAX_N]; vector<int> e[MAX_N]; void dfs(int u, int father, int depth) { fa[u][0] = father, dep[u] = depth; for (int k = 1; k < BITS; k++) fa[u][k] = fa[fa[u][k - 1]][k - 1]; in[u] = ++tim, dfn[tim] = u; for (int v : e[u]) if (v != father) dfs(v, u, depth + 1); out[u] = ++tim, dfn[tim] = u; } int LCA(int x, int y) { if (dep[x] > dep[y]) swap(x, y); for (int k = BITS - 1; ~k; k--) if (dep[x] <= dep[fa[y][k]]) y = fa[y][k]; if (x == y) return x; for (int k = BITS - 1; ~k; k--) if (fa[x][k] != fa[y][k]) x = fa[x][k], y = fa[y][k]; return fa[x][0]; } int tot1, tot2, pos[MAX_N], cnt[MAX_N]; long long cur, ans[MAX_N]; bool vis[MAX_N]; struct Modify { int x, y; } mod[MAX_N]; struct Query { int l, r, p, t, id; bool operator < (const Query& rhs) const { if (pos[l] != pos[rhs.l]) return pos[l] < pos[rhs.l]; if (pos[r] != pos[rhs.r]) return pos[r] < pos[rhs.r]; return t < rhs.t; } } qry[MAX_N]; inline void add(int idx) { cur += 1ll * v[c[idx]] * w[++cnt[c[idx]]]; } inline void del(int idx) { cur -= 1ll * v[c[idx]] * w[cnt[c[idx]]--]; } inline void move(int idx) { vis[idx] ? del(idx) : add(idx), vis[idx] ^= 1; } inline void update(int i) { if (vis[mod[i].x]) del(mod[i].x); swap(c[mod[i].x], mod[i].y); if (vis[mod[i].x]) add(mod[i].x); } void Mo_s_algorithm_on_trees() { const int size = pow(n * 2, 0.667); for (int i = 1; i <= n * 2; i++) pos[i] = (i - 1) / size + 1; sort(qry + 1, qry + tot2 + 1); int l = 1, r = 0, t = 0; for (int i = 1; i <= tot2; i++) { int ql = qry[i].l, qr = qry[i].r; int qt = qry[i].t, qp = qry[i].p; while (l > ql) move(dfn[--l]); while (r < qr) move(dfn[++r]); while (l < ql) move(dfn[l++]); while (r > qr) move(dfn[r--]); while (t < qt) update(++t); while (t > qt) update(t--); if (qp) move(dfn[qp]); ans[qry[i].id] = cur; if (qp) move(dfn[qp]); } } int main() { cin >> n >> m >> q; for (int i = 1; i <= m; i++) cin >> v[i]; for (int i = 1; i <= n; i++) cin >> w[i]; for (int i = 1, a, b; i < n; i++) { cin >> a >> b; e[a].push_back(b); e[b].push_back(a); } dfs(1, 0, 1); for (int i = 1; i <= n; i++) cin >> c[i]; for (int i = 1, type, x, y; i <= q; i++) { cin >> type >> x >> y; if (type == 0) mod[++tot1] = Modify{x, y}; else { tot2++; if (in[x] > in[y]) swap(x, y); int lca = LCA(x, y); if (lca == x) qry[tot2] = Query{in[x], in[y], 0, tot1, tot2}; else qry[tot2] = Query{out[x], in[y], lca, tot1, tot2}; } } Mo_s_algorithm_on_trees(); for (int i = 1; i <= tot2; i++) cout << ans[i] << '\n'; return 0; } ```
最新发布
08-10
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值