[WC2013]糖果公园

题意

给你一棵树,每个点有个颜色

每次询问你一条路径求cvalccntci=1worthi∑cvalc∑i=1cntcworthi

valval表示该颜色的价值,cntcnt表示其出现的次数,woethiwoethi表示第ii次出现的价值

带修改


题解

先求出dfs序把树变成序列

考虑向右扩展一个点,这个贡献我们是可以O(1)O(1)算出来的

假设扩展出的点是的颜色是c,c,那么Δ=valc×worthcntc+1Δ=valc×worthcntc+1

所以可以考虑用带修改树上莫队来求

但是直接用dfsdfs序去扩展的话显然会出问题

因为他会先去扫完起点的子树,产生多余的贡献

考虑怎么去掉多余的贡献,把树变成一个长2n2n括号序列

这样的话扫的过程中起点的子树里的点肯定会被扫两次(一进一出)

连续做两次之后贡献为00,我们可以想到异或

即开一个vis数组,每次访问就一个点uu,就visu^=1=1

但是注意到几个问题

1.1.如果lcalca不是路径端点是不会被计算的

考虑样例的括号序列1244332112443321

询问434→3,那么我们得到的区间是[3,5][3,5]

发现22没有被算进来,这个要特判

当然如果起点就是lca就不需要管了

2.2.如果起点不是lcalca,那么他的贡献是不会被计算的

同样是上面那个例子

我们可以看到44的贡献被算两次抵消掉了

所以这种情况也要特判

修改和普通带修改莫队一样,只要加一维时间即可

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
    while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
template<class T>inline void we(T x){
    if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=2e5+5;
typedef int arr[N];
typedef long long ll;
struct Q{
    int l,r,x,y,z,id;
    inline bool operator<(const Q b)const{
        if(x^b.x)return x<b.x;
        if(y^b.y)return x&1?y<b.y:y>b.y;
        return (x^y)&1?z<b.z:z>b.z;
    }
}q[N];
struct T{int x,c;}t[N];
struct eg{int nx,to;}e[N];
int n,m,ce,dft,Sz,Sq,St,L,R,G;arr fa,fi,sz,val,wor,pos,lis,dep,son,top,col,vis,cnt;ll Now,ans[N];
void dfs(int u){
    dep[u]=dep[fa[u]]+(sz[u]=1);
    go(u)if(v^fa[u]){
        fa[v]=u;dfs(v),sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
}
void dfs(int u,int t){
    top[u]=t;lis[pos[u]=++dft]=u;
    if(son[u])dfs(son[u],t);
    go(u)if(v^fa[u]&&v^son[u])dfs(v,v);
    lis[++dft]=u;
}
inline int lca(int u,int v){
    for(;top[u]^top[v];dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]]);
    return dep[u]<dep[v]?u:v;
}
inline void add(int u,int v){e[++ce]={fi[u],v},fi[u]=ce;}
inline void sol(int x){int c=col[x];
    (vis[x]^=1)?Now+=(ll)wor[++cnt[c]]*val[c]:Now-=(ll)wor[cnt[c]--]*val[c];
}
inline void mdy(int i){
    int u=t[i].x,x=t[i].c,y=col[u];
    vis[u]?Now+=(ll)wor[++cnt[x]]*val[x]-(ll)wor[cnt[y]--]*val[y]:0;
    t[i].c=y,col[u]=x;
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    sd(n),sd(m);int x,y,u,v,g=0;sd(x);
    fp(i,1,m)sd(val[i]);
    fp(i,1,n)sd(wor[i]);m=x;
    fp(i,2,n)sd(u),sd(v),add(u,v),add(v,u);
    fp(i,1,n)sd(col[i]);
    dfs(1),dfs(1,1);
    while(m--){
        sd(x);
        if(x){
            sd(x),sd(y);if(pos[x]>pos[y])swap(x,y);
            q[++Sq]={x,y,pos[x],pos[y],St,Sq};
        }else sd(x),sd(y),t[++St]={x,y};
    }Sz=pow(n,St?2.0/3:1.0/2);
    fp(i,1,Sq)q[i].x/=Sz,q[i].y/=Sz;
    sort(q+1,q+Sq+1);L=pos[q[1].l],R=L-1;
    fp(i,1,Sq){
        x=pos[u=q[i].l],y=pos[v=q[i].r],g=q[i].z;
        while(L>x)sol(lis[--L]);
        while(R<y)sol(lis[++R]);
        while(L<x)sol(lis[L++]);
        while(R>y)sol(lis[R--]);
        while(G<g)mdy(++G);
        while(G>g)mdy(G--);
        int p=lca(u,v);
        if(u^p){sol(u);if(v^p)sol(p);}
        ans[q[i].id]=Now;
        if(u^p){sol(u);if(v^p)sol(p);}
    }
    fp(i,1,Sq)we(ans[i]);
return Ot(),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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值