UOJ 58 糖果公园

Problem :

给一棵n个点的树,每个点上有一种颜色,对于一条路径上的点,若 i 颜色第 j 次出现对该路径权值的贡献为 w[i] * c[j], 每次询问一条路径的权值,或者修改某个点的颜色。

Solution :

树上的带修改的莫队。

使用dfs序来对左右端点进行分块。

第一关键字分块排序左端点,第二关键字分块排序右端点,第三关键字排序询问顺序。

用S(v, u)代表 v到u的路径上的结点的集合。

用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)

(有公式恐惧症的不要走啊 T_T)

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

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

引用自vfk

#include <bits/stdc++.h>
using namespace std; const int INF = 1e9 + 7;
const int N = 1000008;
int n, m, q, block_size, block_num; vector <int> vec[N];
int cv[N], cw[N], cnt[N], a[N], b[N], vis[N];
int belong[N], num, tot1, tot2;
int fa[N][20], dep[N];
stack <int> st;
long long ans[N], sum; struct query1
{
int u, v, ub, vb, x, id;
query1(){}
query1(int u, int v, int x, int id) : u(u), v(v), x(x), id(id)
{
ub = belong[u];
vb = belong[v];
}
bool operator < (const query1 &b) const
{
if (ub != b.ub) return ub < b.ub;
if (vb != b.vb) return vb < b.vb;
return x < b.x;
}
}q1[N];
struct query2
{
int pos, x, y;
query2(){}
query2(int pos, int x, int y) : pos(pos), x(x), y(y){}
}q2[N];
void stpop(int &num)
{
++block_num;
for (int i = 1; i <= num; ++i)
{
int p = st.top(); st.pop();
belong[p] = block_num;
}
num = 0;
}
int dfs(int u)
{
int num = 1;
st.push(u);
for (auto v : vec[u])
{
if (v == fa[u][0]) continue;
fa[v][0] = u; dep[v] = dep[u] + 1;
num += dfs(v);
if (num >= block_size) stpop(num);
}
return num;
}
int get_lca(int u, int v)
{
if (dep[u] < dep[v]) swap(u, v);
int d = dep[u] - dep[v];
for (int i = 19; i >= 0; --i)
if (d & (1 << i))
u = fa[u][i];
if (u == v) return u;
for (int i = 19; i >= 0; --i)
if (fa[u][i] != fa[v][i])
{
u = fa[u][i];
v = fa[v][i];
}
return fa[u][0];
}
void init()
{
block_size = pow(n, 2.0 / 3); block_num = 0;
for (int i = 1; i <= m; ++i) cin >> cv[i];
for (int i = 1; i <= n; ++i) cin >> cw[i];
for (int i = 1; i <= n; ++i) vec[i].clear();
for (int i = 1; i < n; ++i)
{
int u, v; cin >> u >> v;
vec[u].push_back(v);
vec[v].push_back(u);
}
fa[1][0] = 0; dep[1] = 1;
int num = dfs(1);
if (num != 0) stpop(num);
assert(st.empty());
for (int i = 1; i < 20; ++i)
for (int j = 1; j <= n; ++j)
fa[j][i] = fa[fa[j][i - 1]][i - 1];
for (int i = 1; i <= n; ++i) cin >> a[i], b[i] = a[i];
tot1 = tot2 = 0;
for (int i = 1; i <= q; ++i)
{
int t, x, y; cin >> t >> x >> y;
if (t == 0)
{
q2[++tot2] = query2(x, b[x], y);
b[x] = y;
}
else
{
++tot1;
q1[tot1] = query1(x, y, tot2, tot1);
}
}
sort(q1 + 1, q1 + tot1 + 1);
for (int i = 1; i <= n; ++i) vis[i] = 0;
for (int i = 1; i <= m; ++i) cnt[i] = 0;
}
void update(int pos)
{
if (vis[pos])
{
sum -= (long long)cw[cnt[a[pos]]] * cv[a[pos]];
cnt[a[pos]]--;
}
else
{
cnt[a[pos]]++;
sum += (long long)cw[cnt[a[pos]]] * cv[a[pos]];
}
vis[pos] ^= 1;
}
void change(int pos, int x)
{
if (vis[pos])
{
update(pos);
a[pos] = x;
update(pos);
}
else a[pos] = x;
}
void work(int u, int v)
{
int lca = get_lca(u, v);
while (u != lca)
{
update(u);
u = fa[u][0];
}
while (v != lca)
{
update(v);
v = fa[v][0];
}
}
void solve()
{
sum = 0;
for (int i = 1; i <= q1[1].x; ++i) change(q2[i].pos, q2[i].y);
work(q1[1].u, q1[1].v);
update(get_lca(q1[1].u, q1[1].v));
ans[q1[1].id] = sum;
update(get_lca(q1[1].u, q1[1].v));
for (int i = 2, u = q1[1].u, v = q1[1].v, x = q1[1].x; i <= tot1; u = q1[i].u, v = q1[i].v, x = q1[i].x, ++i)
{
for (int j = x + 1; j <= q1[i].x; ++j) change(q2[j].pos, q2[j].y);
for (int j = x; j >= q1[i].x + 1; --j) change(q2[j].pos, q2[j].x);
work(u, q1[i].u);
work(v, q1[i].v);
update(get_lca(q1[i].u, q1[i].v));
ans[q1[i].id] = sum;
update(get_lca(q1[i].u, q1[i].v));
}
for (int i = 1; i <= tot1; ++i) cout << ans[i] << endl;
}
int main()
{
cin.sync_with_stdio(0);
while (cin >> n >> m >> q)
{
init();
solve();
}
}

UOJ 58 (树上带修改的莫队)的更多相关文章

  1. BZOJ 2120: 数颜色 带修改的莫队算法 树状数组套主席树

    https://www.lydsy.com/JudgeOnline/problem.php?id=2120 标题里是两种不同的解法. 带修改的莫队和普通莫队比多了个修改操作,影响不大,但是注意一下细节 ...

  2. 【BZOJ】2120: 数颜色 带修改的莫队算法

    [题意]给定n个数字,m次操作,每次询问区间不同数字的个数,或修改某个位置的数字.n,m<=10^4,ai<=10^6. [算法]带修改的莫队算法 [题解]对于询问(x,y,t),其中t是 ...

  3. P1903 [国家集训队]数颜色 / 维护队列 带修改的莫队

    \(\color{#0066ff}{ 题目描述 }\) 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. Q L R代表询问你从第L支 ...

  4. UVA - 12345 带修改的莫队

    题意显然:给出初始序列,单点修改,区间查询元素的种类. 由于时限过宽,暴力可过. 比较优秀的解法应该是莫队. 带修改的莫队题解可以看https://www.luogu.org/blog/user126 ...

  5. codeforces 940F 带修改的莫队

    F. Machine Learning time limit per test 4 seconds memory limit per test 512 megabytes input standard ...

  6. Machine Learning CodeForces - 940F(带修改的莫队)

    题解原文地址:https://www.cnblogs.com/lujiaju6555/p/8468709.html 给数组a,有两种操作,1 l r查询[l,r]中每个数出现次数的mex,注意是出现次 ...

  7. BZOJ2120 数颜色(带修改的莫队算法)

    Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜 ...

  8. BZOJ 2120 数颜色(带修改的莫队)

    2120: 数颜色 Time Limit: 6 Sec  Memory Limit: 259 MB Submit: 3478  Solved: 1342 [Submit][Status][Discus ...

  9. Luogu P1903 BZOJ 2120 数颜色 带修改的莫队

    https://www.luogu.org/problemnew/show/P1903 之前切过这道题,复习莫队再切一遍,不过我之前写的是主席树和树状数组,也不知道我当时怎么想的…… 这个题卡常我没写 ...

随机推荐

  1. html语法第 -2

    1 <html> 2 <head> 3 <title>这是第一节课网页标题</title> 4 <meta charset="UTF-8 ...

  2. currentStyle getComputedStyle兼容

    function getStyle(obj,attr){ if(obj.currentStyle) {return obj.currentStyle[attr]} else{ return getCo ...

  3. ios 从相册视频中获取视频截图

    //给image添加个分类 +(UIImage *)getImage:(NSURL: *)videoURL { AVURLAsset *asset = [[AVURLAsset alloc] init ...

  4. 磁盘格式化mke2fs

    -b 设置每个块的大小,当前支持1024,2048,40963种字节 -i 给一个inode多少容量 -c 检查磁盘错误,仅执行一次-c时候,会进行快速读取测试:-c -c会测试读写,会很慢 -L 后 ...

  5. xcode6的项目中虚拟键盘无法弹出

    这是因为Xcode6中的模拟器键盘设置跟之前的版本不一样了.之前版本是模拟器的键盘和电脑的键盘都可以使用,但是Xcode6的模拟器键盘只能使用一种,即要么是模拟器键盘,要么是电脑键盘.快捷键切换键盘类 ...

  6. 消息中间件与RPC的区别

    消息中间件和消息通信与RPC各自具有怎样的优势,如何互补消息中间件主要实现的是异步.弹性消息以及队列,弹性消息有时可以借助于外存从而一定程度上可以实现峰值缓存,有效均衡服务器端压力,同时消息可以进行一 ...

  7. Android掌中游斗地主游戏源码完整版

    源码大放送-掌中游斗地主(完整版),集合了单机斗地主.网络斗地主.癞子斗地主等,有史以来最有参考价值的源码,虽然运行慢了一点但是功能正常,用的是纯java写的. 项目详细说明:http://andro ...

  8. JavaFX Chart设置数值显示

    一.XYChart import javafx.application.Application;import javafx.geometry.NodeOrientation;import javafx ...

  9. centos7 取消Ctrl+Alt+Del重启功能

    转载:http://www.cnblogs.com/huangjc/p/4536620.html Linux默认允许任何人按下Ctrl+Alt+Del来重启系统.但是在生产环境中,应该停用按下Ctrl ...

  10. VMware 彻底删除虚拟机操作系统的方法

    方法一 首先,都需要点击左边的虚拟机列表,选中你要删除的操作系统 点击VMwae上方的虚拟机-管理-从硬盘删除. 方法二 右键左侧列表中要删除的系统-移除. 然后在硬盘上找到其所在文件夹,直接按SHI ...