题目链接:http://www.spoj.com/problems/COT2/

参考博客:http://www.cnblogs.com/xcw0754/p/4763804.html
上面这个人推导部分写的简洁明了,方便理解,但是最后的分情况讨论有些迷,感觉是不必要的,更简洁的思路看下面的博客

传送门:http://blog.csdn.net/kuribohg/article/details/41458639

题意是这样的,给你一棵无根树,给你m个查询,每次查询输出节点x到节点y路径上不同颜色的节点有多少个(包括xy)

没什么说的,直接上代码吧

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
int const MAX_N=+;
int const MAX_M=+;
int n,m,ans,nowLca,lastL,lastR,tempAns;
int color[MAX_N];
vector<int> allColor;
vector<int>::iterator it;
int lin[MAX_N],e_cnt;
struct Query{
int x;
int y;
int id;
int ans;
}query[MAX_M];
struct Edge{
int next;
int y;
}e[*MAX_N];
void insert(int u,int v){
e[++e_cnt].next=lin[u];
e[e_cnt].y=v;
lin[u]=e_cnt;
}
int b_len,b_cnt,bk[MAX_N];
int fa[MAX_N][],deep[MAX_N];
int n_cnt,dfn[MAX_N],top,stack[MAX_N];
bool vis[MAX_N];
void dfs(int u){
dfn[u]=++n_cnt;
int btm=top;
for(int i=lin[u];i;i=e[i].next){
int v=e[i].y;
if(!dfn[v]){
fa[v][]=u;
deep[v]=deep[u]+;
dfs(v);
if(top-btm>=b_len){
b_cnt++;
while(top>btm){
bk[stack[top--]]=b_cnt;
}
}
}
}
stack[++top]=u;
}
bool queryCmp_xkb_ydfn(Query a,Query b){
if(bk[a.x]==bk[b.x])return dfn[a.y]<dfn[b.y];
return bk[a.x]<bk[b.x];
}
bool queryCmp_id(Query a,Query b){
return a.id<b.id;
}
int lca(int u,int v){
if(deep[u]<deep[v])swap(u,v);
for(int i=;~i;i--)
if(deep[fa[u][i]]>=deep[v])
u=fa[u][i];
if(u == v) return u;
for(int i=;~i;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][];
}
int c_cnt[MAX_N];
void MoveToLca(int u){
for(;u!=nowLca;u=fa[u][]){
if(vis[u]){
vis[u]=false;
c_cnt[color[u]]--;
if(!c_cnt[color[u]])ans--;
}else{
vis[u]=true;
if(!c_cnt[color[u]])ans++;
c_cnt[color[u]]++;
}
}
}
int GetAns(int x,int u,int v){
int tlca=lca(u,v);
if(c_cnt[color[tlca]])return x;
else return x+;
} void FirstMo(){
int L=query[].x,R=query[].y;
nowLca=lca(L,R);
MoveToLca(L);
MoveToLca(R);
query[].ans=GetAns(ans,L,R);
lastL=L;lastR=R;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
allColor.clear();
//read colors
for(int i=;i<=n;i++){
scanf("%d",&color[i]);
allColor.push_back(color[i]);
}
//Discretization the color
sort(allColor.begin(),allColor.end());
it=unique(allColor.begin(),allColor.end());
allColor.resize(distance(allColor.begin(),it));
for(int i=;i<=n;i++){
color[i]=lower_bound(allColor.begin(),allColor.end(),color[i])-allColor.begin()+;
}
//read the tree
memset(lin,,sizeof(lin));
e_cnt=;
for(int i=;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
insert(u,v);
insert(v,u);
}
//divide in blocks
//prepare for lca: get deep, dfs order, get father
b_len=sqrt((double)n);
b_cnt=;
n_cnt=;
top=;
deep[]=,deep[]=;
memset(fa,,sizeof(fa));
memset(dfn,,sizeof(dfn));
memset(vis,,sizeof(vis));
memset(stack,,sizeof(stack));
dfs();
b_cnt++;
while(top){
bk[stack[top--]]=b_cnt;
}
for(int i = ; (<<i) <= n; i++)
for(int j = ; j <= n; j++)
fa[j][i] = fa[fa[j][i-]][i-];
//read the query
for(int i=;i<=m;i++){
scanf("%d%d",&query[i].x,&query[i].y);
query[i].id=i;
if(dfn[query[i].x]>dfn[query[i].y])
swap(query[i].x,query[i].y);
}
//mo's algorithm
sort(query+,query+m+,queryCmp_xkb_ydfn);
memset(vis,,sizeof(vis));
memset(c_cnt,,sizeof(c_cnt));
lastL=,lastR=,ans=;
for(int i=;i<=m;i++){
int L=query[i].x,R=query[i].y;
nowLca=lca(L,lastL);
MoveToLca(L);
MoveToLca(lastL);
nowLca=lca(R,lastR);
MoveToLca(R);
MoveToLca(lastR);
query[i].ans=GetAns(ans,L,R);
lastL=L;lastR=R;
}
sort(query+,query+m+,queryCmp_id);
for(int i=;i<=m;i++){
printf("%d\n",query[i].ans);
}
}
return ;
}

wa了无数发,最后发现是dfs的时候用vis数组判断点是否访问过,结果忘记初始化vis[1]了。。。。好蠢,后来改成了直接用dfn数组判断是否访问过了,本意是节省资源,结果居然A了。。。人生处处是惊喜。

这里顺便总结下lca写法吧。

这里的是倍增法求LCA

int fa[MAX_N][20],deep[MAX_N];
int dfn[MAX_N];

void dfs(int u){
    dfn[u]=++n_cnt;
    for(int i=lin[u];i;i=e[i].next){
        int v=e[i].y;
        if(!dfn[v]){
            fa[v][0]=u;
            deep[v]=deep[u]+1;
            dfs(v);
        }
    }
}
int lca(int u,int v){
    if(deep[u]<deep[v])swap(u,v);
    for(int i=17;~i;i--)
        if(deep[fa[u][i]]>=deep[v])
            u=fa[u][i];
    if(u == v) return u;
    for(int i=17;~i;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}

int main()
{
        n_cnt=0;
        deep[0]=0,deep[1]=1;
        memset(fa,0,sizeof(fa));
        memset(dfn,0,sizeof(dfn));
        dfs(1);

for(int i = 1; (1<<i) <= n; i++)
            for(int j = 1; j <= n; j++)
                fa[j][i] = fa[fa[j][i-1]][i-1];
            return 0;
}

dfn换成vis也是一样的

首先初始化deep数组表示节点深度,然后是fa[i][j]表示节点i的第2^j的父亲节点是什么,

先dfs求出deep和直系父亲

然后双重循环处理fa数组

lca的时候,先对齐u和v,就是让他们处于同一深度,然后一起向上,直到lca的儿子为止,返回他的父亲,就是lca。

SPOJ COT2 Count on a tree II (树上莫队)的更多相关文章

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

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

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

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

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

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

  4. SP10707 COT2 - Count on a tree II [树上莫队学习笔记]

    树上莫队就是把莫队搬到树上-利用欧拉序乱搞.. 子树自然是普通莫队轻松解决了 链上的话 只能用树上莫队了吧.. 考虑多种情况 [X=LCA(X,Y)] [Y=LCA(X,Y)] else void d ...

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

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

  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

    COT2 - Count on a tree II http://www.spoj.com/problems/COT2/ #tree You are given a tree with N nodes ...

  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/ You are given a tree with N nodes.The tree nodes are numbere ...

随机推荐

  1. P1304 哥德巴赫猜想

    题目描述 输入N(N<=10000),验证4~N所有偶数是否符合哥德巴赫猜想. (N为偶数). 如果一个数,例如10,则输出第一个加数相比其他解法最小的方案.如10=3+7=5+5,则10=5+ ...

  2. 原生js做h5小游戏之打砖块

    前言 首先,先说明一下做这个系列的目的:其实主要源于博主希望熟练使用 canvas 的相关 api ,同时对小游戏的实现逻辑比较感兴趣,所以希望通过这一系列的小游戏来提升自身编程能力:关于 es6 语 ...

  3. 在YII2中使用memcached

    一.在本地安装Memcached服务器和安装memcached扩展 http://www.cnblogs.com/songziqing/p/5896742.html http://www.cnblog ...

  4. 单元测试工具 unitils

    Unitils模块组件 Unitils通过模块化的方式来组织各个功能模块,采用类似于Spring的模块划分方式,如unitils-core.unitils-database.unitils-mock等 ...

  5. (转)RabbitMQ学习之集群部署

    http://blog.csdn.net/zhu_tianwei/article/details/40931971 我们先搭建一个普通集群模式,在这个模式基础上再配置镜像模式实现高可用,Rabbit集 ...

  6. 传入class、id name 的函数封装

    function chooseDate(idName){ 2 $('#' + idName).click(function(){ //执行函数 4 }); 5 }; 6 //传入的 dataOne 就 ...

  7. JDBC连接MySQL数据库(一)——数据库的基本连接

    JDBC的概念在使用之前我们先了解一下JDBC的概念, JDBC的全称是数据库连接(Java Database Connectivity),它是一套用于执行SQL语句时的API,应用程序可以通过这套A ...

  8. 探索JS引擎工作原理 (转)

    这篇文章从相对底层的角度介绍了js引擎的工作 引入了 静态作用域 执行环境上下文(context) 等概念 , http://www.cnblogs.com/onepixel/p/5090799.ht ...

  9. vue 函数配置项watch以及函数 $watch 源码分享

    Vue双向榜单的原理     大家都知道Vue采用的是MVVM的设计模式,采用数据驱动实现双向绑定,不明白双向绑定原理的需要先补充双向绑定的知识,在watch的处理中将运用到Vue的双向榜单原理,所以 ...

  10. 记Spring搭建功能完整的个人博客「Oyster」全过程[其二] Idea中Maven+SpringBoot多模块项目开发的设计和各种坑(模块间依赖和打包问题)

    大家好嘞,今天闲着没事干开写写博客,记录一下Maven+SpringBoot的多模块设计和遇到的坑. 多模块设计 简单说明一下截止目前的需求: 需要RESTful API:对文章.标签.分类和评论等的 ...