SPOJ COT2 树上找路径上不同值的个数
题目大意
给出多个询问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 树上找路径上不同值的个数的更多相关文章
- poj 3417 Network (LCA,路径上有值)
题意: N个点,构成一棵树.给出这棵树的结构. M条边,(a1,b1)...(am,bm),代表给树的这些点对连上边.这样就形成了有很多环的一个新"树". 现在要求你在原树中断一条 ...
- spoj COT2(树上莫队)
模板.树上莫队的分块就是按dfn分,然后区间之间转移时注意一下就好.有个图方便理解http://blog.csdn.net/thy_asdf/article/details/47377709: #in ...
- 「日常训练&知识学习」莫队算法(二):树上莫队(Count on a tree II,SPOJ COT2)
题意与分析 题意是这样的,给定一颗节点有权值的树,然后给若干个询问,每次询问让你找出一条链上有多少个不同权值. 写这题之前要参看我的三个blog:Codeforces Round #326 Div. ...
- 最短路径(给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。)
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小. 说明:每次只能向下或者向右移动一步. 例: 输入: [ [1,3,1], [1,5,1], [ ...
- 牛客网 桂林电子科技大学第三届ACM程序设计竞赛 D.寻找-树上LCA(树上a到b的路径上离c最近的点)
链接:https://ac.nowcoder.com/acm/contest/558/D来源:牛客网 寻找 小猫在研究树. 小猫在研究树上的距离. 给定一棵N个点的树,每条边边权为1. Q次询问,每次 ...
- 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 ...
- SPOJ COT2 Count on a tree II 树上莫队算法
题意: 给出一棵\(n(n \leq 4 \times 10^4)\)个节点的树,每个节点上有个权值,和\(m(m \leq 10^5)\)个询问. 每次询问路径\(u \to v\)上有多少个权值不 ...
- SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)
题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无 ...
- SPOJ COT2 Count on a tree II (树上莫队)
题目链接:http://www.spoj.com/problems/COT2/ 参考博客:http://www.cnblogs.com/xcw0754/p/4763804.html上面这个人推导部分写 ...
随机推荐
- Android软键盘弹出时把布局顶上去的解决方法
原文: 解决Andriod软键盘出现把原来的布局给顶上去的方法(转) 链接:http://blog.sina.com.cn/s/blog_9564cb6e0101g2eb.html 决方法,在main ...
- Python Paramiko模块安装和使用
1.简介 大家会发现,常见的解决方法都会需要对远程服务器必要的配置,如果远程服务器只有一两台还好说,如果有N台,还需要逐台进行配置,或者需要使用代码进行以上操作时,上面的办法就不太方便了. 使用par ...
- LocalStorage在Chrome里的实现
前段时间我们在实现CanTK-Runtime时,也曾在V8基础上模拟过浏览器的LocaleStorage功能,其实现非常简单:每个domain的数据使用的单独文件存储,因为同一时间只有一个游戏运行,所 ...
- webpages框架中使用Html.TextArea()在前台显示多行信息时,如何进行大小、样式的设置
环境:vs2015 webpages框架+razor语法: 目的:服务器进行数据更新操作后,在前台显示更新的相关信息: 后台代码:将更新条数等相关信息存储在一个变量中: @{ var serverIn ...
- <mvc:annotation-driven />
<mvc:annotation-driven/>相当于注册了DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter两个 ...
- Azure 云平台用 SQOOP 将 SQL server 2012 数据表导入 HIVE / HBASE
My name is Farooq and I am with HDinsight support team here at Microsoft. In this blog I will try to ...
- nodeschool.io 9
~~ JUGGLING ASYNC ~~ 其实就是一个循环,在循环里面输出的顺序,和排列后在外面的顺序不一样,这是为什么呢? 用第三方async包,直接报错了…… This problem is th ...
- Compound Interest Calculator1.0
Compound Interest Calculator1.0 客户说:帮我开发一个复利计算软件. 计算:本金为100万,利率或者投资回报率为3%,投资年限为30年,那么,30年后所获得的利息收入:按 ...
- python中urllib和urllib2的简单用法
import urllib #引入urllib模块,这里用urllib2也可以 fpage = urllib.urlopen( url ) #打开网页:例如url=‘http://www.xxx.co ...
- response.getWriter().write()与out.print()的区别
1.首先介绍write()和print()方法的区别: (1).write():仅支持输出字符类型数据,字符.字符数组.字符串等 (2).print():可以将各种类型(包括Object)的数据通 ...