题目大意

给出多个询问u , v , 求出u-v路径上点权值不同的个数

开始做的是COT1,用主席树写过了,理解起来不难

很高兴的跑去做第二道,完全跟普通数组区间求k个不同有很大区别,完全没思路

膜拜http://www.cnblogs.com/oyking/p/4265823.html

这里利用莫队思想来做,在树上分块,尽可能让相连部分作为一个联通块,那么就在dfs过程中加个手写栈,如果回溯上来的时候保存的值的个数超过每块中应有的

个数那么就将他们分到同一个id块

排序也是跟普通莫队上一样,按分块编号排序,这里有两个端点,那么就先将编号小的摆在前面在排序

离线排序做好了,剩下就是转移的问题,从当前一条路径转移到下一条,diff保存了之前记录的路径上不同点的个数

之前是u,v , 现在去curu , curv , 那么先找到u-curu路径上的点,添加进来,v-curv路径上的点添加进来

画个图可以看出这是原来的路径基础上不需要的点会被再多访问一次,那些需要的点要么本身在原基础上不再访问,要么又多访问两次保持不变,像异或一样

所以记录当前点有没有被访问奇数次就行了,多访问就翻转

最后会有多余的点没有被访问到,就是LCA(u,v) LCA(curu , curv) , 这个要在访问一次即可

这里因为是分块的,所以跑的是曼哈顿距离的询问区间,不会超时

 #include <bits/stdc++.h>
using namespace std; #define N 40010
int first[N] , k;
int block[N] , sz;//对应分到的块的编号和每块应含的大小
int _stack[N] , top , cursz , ID;//手写栈
int n , m;
int hash[N] , a[N] , b[N]; struct Edge{
int x , y , next;
Edge(){}
Edge(int x , int y , int next):x(x),y(y),next(next){}
}e[N<<]; void add_edge(int x , int y)
{
e[k] = Edge(x , y , first[x]);
first[x] = k++;
} int dp[N<<][] , id[N<<] , dep[N<<] , No[N] , fa[N] , dfs_clock;
int depth[N]; void add_block(int &cursz , int ID)
{
while(cursz){
block[_stack[--top]] = ID;
// cout<<"IN: "<<ID<<" "<<_stack[top]<<" "<<top<<" "<<sz<<endl;
cursz--;
}
} void dfs(int u , int f , int d)
{
id[++dfs_clock] = u , No[u] = dfs_clock , dep[dfs_clock] = d;
fa[u] = f , depth[u] = d;
for(int i=first[u] ; ~i ; i=e[i].next){
int v = e[i].y;
if(v == f) continue;
dfs(v , u , d+);
id[++dfs_clock] = u , dep[dfs_clock] = d;
}
//树上分块重要部分
cursz++;
_stack[top++] = u;
if(cursz>=sz) add_block(cursz , ++ID);
} void ST(int n)
{
for(int i= ; i<=n ; i++) dp[i][] = i;
for(int j= ; (<<j)<=n ; j++){
for(int i= ; i+(<<j)-<=n ; i++){
int a = dp[i][j-] , b=dp[i+(<<(j-))][j-];
dp[i][j] = dep[a]<dep[b]?a:b;
}
}
} int RMQ(int l , int r)
{
int k=;
while((<<(k+))<=r-l+) k++;
int a = dp[l][k] , b = dp[r-(<<k)+][k];
return dep[a]<dep[b]?a:b;
} int LCA(int u , int v)
{
int x=No[u] , y=No[v];
if(x>y) swap(x , y);
return id[RMQ(x,y)];
} void get_hash(int n){
for(int i= ; i<=n ; i++)
hash[i] = lower_bound(b+ , b+n+ , a[i])-b;
} struct Query{
int u , v , id;
void reset(){
if(block[u]>block[v]) swap(u , v);
}
bool operator<(const Query &m) const{
return block[u]<block[m.u]||(block[u]==block[m.u] && block[v]<block[m.v]);
}
void in(int i){scanf("%d%d" , &u , &v);id=i;}
}qu[]; int ans[] , vis[N] , cnt[N] , diff; void xorNode(int x)
{
// cout<<"xor: "<<x<<endl;
if(vis[x]) vis[x]=false , diff -= (--cnt[hash[x]]==);
else vis[x] = true , diff += (++cnt[hash[x]]==);
} void xorPath(int x , int y)
{
// cout<<"path: "<<x<<" "<<y<<endl;
if(depth[x]<depth[y]) swap(x , y);
while(depth[x]>depth[y]){
xorNode(x);
x = fa[x];
}
while(x!=y){
xorNode(x) , xorNode(y);
x = fa[x] , y=fa[y];
}
} void debug()
{
// for(int i=1 ; i<=n ; i++) cout<<"i: "<<block[i]<<" "<<hash[i]<<" fa: "<<fa[i]<<" "<<depth[i]<<endl;
cout<<"test: "<<LCA(,)<<" "<<LCA( , )<<endl;
}
void solve()
{
sz = (int)sqrt(n+0.5);
dfs( , , );
ST(n*-);
add_block(cursz , ++ID);
// debug();
for(int i= ; i<m ; i++){
qu[i].in(i);
qu[i].reset();
}
sort(qu , qu+m);
memset(vis , , sizeof(vis));
diff = ;
int curu= , curv=;
xorNode();
for(int i= ; i<m ; i++){
xorPath(curu , qu[i].u);
xorPath(curv , qu[i].v);
xorNode(LCA(curu , curv));
xorNode(LCA(qu[i].u , qu[i].v));
curu = qu[i].u , curv = qu[i].v;
ans[qu[i].id] = diff;
// cout<<"----华丽的分割线---"<<endl;
}
for(int i= ; i<m ; i++) printf("%d\n" , ans[i]);
} int main()
{
// freopen("in.txt" , "r" , stdin);
scanf("%d%d" , &n , &m);
for(int i= ; i<=n ; i++) scanf("%d" , &a[i]) , b[i]=a[i];
sort(b+ , b+n+);
get_hash(n);
int x , y;
memset(first , - , sizeof(first));
top = cursz = ID = k = ;
for(int i= ; i<n ; i++){
scanf("%d%d" , &x , &y);
add_edge(x , y);
add_edge(y , x);
}
solve();
return ;
}

SPOJ COT2 树上找路径上不同值的个数的更多相关文章

  1. poj 3417 Network (LCA,路径上有值)

    题意: N个点,构成一棵树.给出这棵树的结构. M条边,(a1,b1)...(am,bm),代表给树的这些点对连上边.这样就形成了有很多环的一个新"树". 现在要求你在原树中断一条 ...

  2. spoj COT2(树上莫队)

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

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

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

  4. 最短路径(给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。)

    给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小. 说明:每次只能向下或者向右移动一步. 例: 输入: [ [1,3,1], [1,5,1], [ ...

  5. 牛客网 桂林电子科技大学第三届ACM程序设计竞赛 D.寻找-树上LCA(树上a到b的路径上离c最近的点)

    链接:https://ac.nowcoder.com/acm/contest/558/D来源:牛客网 寻找 小猫在研究树. 小猫在研究树上的距离. 给定一棵N个点的树,每条边边权为1. Q次询问,每次 ...

  6. 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  ...

  7. SPOJ COT2 Count on a tree II 树上莫队算法

    题意: 给出一棵\(n(n \leq 4 \times 10^4)\)个节点的树,每个节点上有个权值,和\(m(m \leq 10^5)\)个询问. 每次询问路径\(u \to v\)上有多少个权值不 ...

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

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

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

    题目链接:http://www.spoj.com/problems/COT2/ 参考博客:http://www.cnblogs.com/xcw0754/p/4763804.html上面这个人推导部分写 ...

随机推荐

  1. adb 查看日志信息

    adb logcat 详解  (1) 下面命令将只会显示AndroidRuntime类型的Error消息:         adb  logcat -s AndroidRuntime  (2) 显示全 ...

  2. ListView 使用详解

    在ListView中设置Selector为null会报空指针?mListView.setSelector(null);//空指针试试下面这种:mListView.setSelector(new Col ...

  3. 项目二:使用机器学习(SVM)进行基因预测

    SVM软件包 LIBSVM -- A Library for Support Vector Machines(本项目所用到的SVM包)(目前最新版:libsvm-3.21,2016年7月8日) C-S ...

  4. x+y = ((x&y)<<1) + (x^y) 证明

    法一:我们考虑x,y在二进制表示时候,按位相加其中第i位xi+yi = ((xi&yi)<<1) + (xi^yi)其中(xi&yi)<<1表示当xi和yi都是 ...

  5. Appium 切换上下文环境

    Appium 切换上下文环境,代码如下: private void switchToContext(String sContext) { LogManager.getLogger(this.getCl ...

  6. jsp 页面 jstl 日期的计算

    1. <% page import ="java.util.Date"%> 2. <% Date date = new Date(); date = new Da ...

  7. python 暴力破解密码脚本

    python 暴力破解密码脚本 以下,仅为个人测试代码,环境也是测试环境,暴力破解原理都是一样的, 假设要暴力破解登陆网站www.a.com 用户 testUser的密码, 首先,该网站登陆的验证要支 ...

  8. 华为面试题——约瑟夫问题的C++简单实现(循环链表)

    /*     author:jiangxin     Blog:http://blog.csdn.net/jiangxinnju     Function:method of Josephus que ...

  9. android内存耗用:VSS/RSS/PSS/USS

    VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)  不是真实当前应用进程所占用的内存. 内存分配的原理 从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完 ...

  10. mysql 存储过程 demo

    -- 查看存储过程 SHOW PROCEDURE STATUS; -- 显示pro存储过程的详细信息 SHOW CREATE PROCEDURE pro; -- 删除pro存储过程 DROP PROC ...