• 题意: 给一棵树,每次查询u到v路径上有多少不同的点权

首先需要证明这类题目符合区间加减性质

摘选一段vfk大牛的证明

用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) xorS(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))上的结点,将它们的存在性取反即可

其实也就是说对于先前位置(preu,prev) 和当前位置 已知T(preu,prev) 可以很方便的计算T(curu,curv)

具体做法如下

求出T(preu,curu) 和 T(prev,curv) 暴力遍历两点之间路径即可

此时T(curu,curv) = T(preu,prev) xor T(preu,curu) xor T(prev,curv)

S(curu,curv) = T(curu,curv) xot lca(curu,curv)


下面考虑如何对树上序列进行分块了

有两种方法

  • 第一种方法:dfs的时候对每个点记录 进栈时间戳f(x) 和 出栈时间戳g(x),得到一个2n的序列
  • 对于查询(x,y) 令f(x) < f(y)
  • 如果 x 是 y 的祖先,考虑从x向下走到y 即区间[f(x) , f(y)]

    显然除了x到y路径上的点 之外 其他在区间[f(x),f(y)]出现的点都出现了两次
  • 如果x 不是 y 的祖先,那么必然是先往上走 再往下,即区间[g(x),f(y)] 再加上lca(x,y)
  • 第二种方法: 考虑对树上关键点的划分,详情见分块有关论文,证明我也没太看懂,大概的理解就是把一些距离相近的点划分成一块,减少块与块之间需要跨越的距离。

第一种方法 序列长度为2n 看起来常数似乎比第二种要大,而且每个点记录两次处理起来麻烦一点,所以我用的是第二种

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int col[N],vis[N],cnt[N];
int pos[N],dfn[N];
int head[N],EN,tot;
int n , m, Siz;
inline void read(int &x){
char c = getchar();
x = 0;
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar();
}
struct Q{
int l , r, id, b;
int x , y;
Q(){};
bool operator < (const Q&rhs)const{
if(b == rhs.b) return dfn[r] < dfn[rhs.r];///左端点先按块排序,再右端点按时间戳排序
return b < rhs.b;
}
}q[N]; int ans[N] , res; struct edge{
int v,nxt;
edge(){};
edge(int v,int nxt):v(v),nxt(nxt){};
}e[N]; void add(int u,int v){
e[EN] = edge(v,head[u]);
head[u] = EN++;
} int f[N][22],dep[N];
int lca(int u,int v){
if(dep[u] < dep[v]) swap(u,v);
int d = dep[u] - dep[v];
for(int i = 0;i <= 20;i++)
if((1<<i) & d) u = f[u][i];
if(u == v) return u;
for(int i = 20;i >= 0;i--)
if(f[u][i] != f[v][i]) u = f[u][i],v = f[v][i];
return f[u][0];
}
int stk[N],top,b_cnt; int dfs(int u,int fa,int d){
for(int i = 1;i <= 20;i++) f[u][i] = f[f[u][i-1]][i-1];
dep[u] = d;
dfn[u] = tot++;
int siz = 0;
for(int i = head[u];~i;i = e[i].nxt){
if(i == fa) continue;
int v = e[i].v;
f[v][0] = u;
siz += dfs(v,i ^ 1,d + 1);
if(siz >= Siz){
while(siz--) pos[stk[top--]] = b_cnt;
b_cnt++;
}
}
stk[++top] = u;
return siz + 1;
}
void init(){
memset(head,-1,sizeof(head));
b_cnt = top = EN = tot = 0;
}
inline void up(int u){
if(!vis[u]) {
if(++cnt[col[u]] == 1) res++;
vis[u] = 1;
}else{
vis[u] = 0;
if(--cnt[col[u]] == 0) res--;
}
}
inline void work(int u,int v){
while(u != v){
if(dep[u] < dep[v]) swap(u,v);
up(u),u = f[u][0];
}
}
map<int,int> mp;
int ID;
/*
8 2
1000000000000 2 9 3 8 5 1000001 1000001
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5
7 8
*/
int main(){ read(n),read(m);
ID = 1;mp.clear();
for(int i = 1;i <= n;i++) {
read(col[i]);
if(!mp[col[i]]) mp[col[i]] = ID++;
col[i] = mp[col[i]];
}
init();
int rt = 0;
for(int i = 1;i < n;i++){
int u , v;
read(u),read(v);
if(!u) rt = v;
else if(!v) rt = u;
else add(u,v),add(v,u);
}
Siz = sqrt(n + 0.5);
dfs(1,-1,1);
while(top) pos[stk[top--]] = b_cnt; for(int i = 0;i < m;i++){
read(q[i].l),read(q[i].r);//read(q[i].x),read(q[i].y);
if(dfn[q[i].l] > dfn[q[i].r]) swap(q[i].l,q[i].r);
q[i].id = i;
q[i].b = pos[q[i].l];
}
sort(q,q + m);
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
res = 0;
int LCA = lca(q[0].l,q[0].r);
work(q[0].l,q[0].r);
up(LCA);
ans[q[0].id] = res;
up(LCA);
for(int i = 1;i < m;i++){
work(q[i-1].l,q[i].l);
work(q[i-1].r,q[i].r);
LCA = lca(q[i].l,q[i].r);
up(LCA);
ans[q[i].id] = res;
up(LCA);
}
for(int i = 0;i < m;i++) printf("%d\n",ans[i]);
return 0;
}

树上莫队 SPOJ COT2的更多相关文章

  1. SPOJ COT2 - Count on a tree II(LCA+离散化+树上莫队)

    COT2 - Count on a tree II #tree You are given a tree with N nodes. The tree nodes are numbered from  ...

  2. spoj COT2 - Count on a tree II 树上莫队

    题目链接 http://codeforces.com/blog/entry/43230树上莫队从这里学的,  受益匪浅.. #include <iostream> #include < ...

  3. spoj COT2(树上莫队)

    模板.树上莫队的分块就是按dfn分,然后区间之间转移时注意一下就好.有个图方便理解http://blog.csdn.net/thy_asdf/article/details/47377709: #in ...

  4. 「日常训练&知识学习」莫队算法(二):树上莫队(Count on a tree II,SPOJ COT2)

    题意与分析 题意是这样的,给定一颗节点有权值的树,然后给若干个询问,每次询问让你找出一条链上有多少个不同权值. 写这题之前要参看我的三个blog:Codeforces Round #326 Div. ...

  5. SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

    题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无 ...

  6. 【SPOJ】Count On A Tree II(树上莫队)

    [SPOJ]Count On A Tree II(树上莫队) 题面 洛谷 Vjudge 洛谷上有翻译啦 题解 如果不在树上就是一个很裸很裸的莫队 现在在树上,就是一个很裸很裸的树上莫队啦. #incl ...

  7. COT2 - Count on a tree II(树上莫队)

    COT2 - Count on a tree II You are given a tree with N nodes. The tree nodes are numbered from 1 to N ...

  8. SP10707 COT2 - Count on a tree II (树上莫队)

    大概学了下树上莫队, 其实就是在欧拉序上跑莫队, 特判lca即可. #include <iostream> #include <algorithm> #include < ...

  9. [SPOJ]Count on a tree II(树上莫队)

    树上莫队模板题. 使用欧拉序将树上路径转化为普通区间. 之后莫队维护即可.不要忘记特判LCA #include<iostream> #include<cstdio> #incl ...

随机推荐

  1. oauth2.0协议接口-第一篇-api逻辑

    开放平台是支持OAuth2.0和RESTful协议的资源分享平台,经过授权的合作伙伴可以读取和写入资讯.用户.文件.数据库等资源. 1.创建数据库表结构 CMSSyncClient(数据同步客户端) ...

  2. 中通快递股份有限公司.net高级面试题

    中通快递分布式技术开发   gc垃圾回收原理 .net中,托管代码的内存管理是自动的,由GC进行管理,而对于非托管代码,则需要.net手动处理 CLR运行时,内存分为:托管堆和栈,其中栈用于存储值类型 ...

  3. php图片压缩-高清晰度

    php高清晰度无损压缩 经常会用到把上传的大图片压缩,特别是体积,在微信等APP应用上,也默认都是有压缩的,那么,怎么样对图片大幅度压缩却仍能保持较高的清晰度呢? 压缩通常是有按比例缩放,和指定宽度压 ...

  4. kivy学习一:安装kivy模块

    现在是看脸的时代,一个程序没有一个漂亮的UI,就像一个深闺中的美女没人欣赏. 当然作为一个小小.............白,没有那么高的要求,当前要先有脸是不? 首选python自家的模块tkinte ...

  5. iOS-UICollectionViewController 介绍

    废话不多说,列几个列子 (几种情况下的做法): 情景一: 介绍:1. 在UIViewController 上加 UICollectionView (用代码 创建 UICollectionView). ...

  6. C语言进阶——分支语句06

    if分支语句分析: if语句用于根据条件选择执行语句 else不能独立存在且总是与在它之前的最近if相匹配 esle语句后可以连接其他if语句 用法如下: if(condition) { //stat ...

  7. [WorldFinal 2012E]Infiltration(dfs+图论)

    Description 题意:给定一个点数为n的竞赛图,求图的最小支配集 n<=75 Solution 如果将竞赛图的一个点删去,这个图还是竞赛图 而竞赛图每个点相连的边数为(n-1),那么删去 ...

  8. Git-补丁文件交互

    版本库间的交互是通过git push和/或git pull命令实现的,这是Git最主要的交互模式,但并不是全部.使用补丁文件是另外一种交互方式,适用于参与者众多的大型项目进行分布式开发. 创建补丁 G ...

  9. 2753: [SCOI2012]滑雪与时间胶囊

    2753: [SCOI2012]滑雪与时间胶囊 Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 2633  Solved: 910 Descriptio ...

  10. MySQL添加和删除字段

    查询表的字段类型: mysql> desc t_template_title; +----------------+--------------+------+-----+---------+- ...