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上面这个人推导部分写 ...
随机推荐
- [Hibernate] - EAGER and LAZY
Hibernate中的字段映射中的Fetch有两种方式:EAGER和LAZY Eager:全部抓取 Lazy:延迟抓取 如果在字段中声明为Eager,那么在取得当前Bean时,同时会抓取Bean中的关 ...
- Python主文件路径和当前模块路径
主执行文件路径sys.argv[0] ...
- 打不开chm文件解决办法
打不开chm文件解决办法.bat regsvr32 itss.dll /sregsvr32 hhctrl.ocx /s
- css制作简单的导航栏
//css代码 #menu{ height: 65px; width:100%; background-color: rgba(0, 0, 0, 0.5);}#menu ul{ list-style: ...
- VR应用里面的Photogrammetry技术是什么
http://www.manew.com/thread-49556-1-1.html 具体使用 http://www.didayin.com/archives/632 软件下载 http://labs ...
- JavaScript document
window -- document用于表现HTML页面当前窗体的内容 document,中文"文档" document是BOM中最重要对象之一 document对象是window ...
- js解码
如代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
- !important css样式
重要性 我们在做网页代码的时,有些特殊的情况需要为某些样式设置具有最高权值,怎么办?这时候我们可以使用!important来解决. 如下代码: p{color:red!important;} p{co ...
- poj-----(2528)Mayor's posters(线段树区间更新及区间统计+离散化)
Mayor's posters Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 43507 Accepted: 12693 ...
- IO流--文件处理
import java.io.*; public class io { public static void main(String[] args) { ListDemo(); File dir = ...