您的位置:首页 > 其它

[SPOJ COT2]-Count on a tree II-树上莫队

2018-01-10 10:22 429 查看

说在前面

乘着这几天大力填补之前留下的坑…

树上分块果然还是使用 王室联邦法 最好了=w=

题目

SPOJ COT2传送门

题目大意

给定一棵有N个节点的树,树上的节点都有各自的权值

现在给出M个询问,询问u到v的路径上有多少种不同的权值

输入输出格式

输入格式:

第一行两个整数N,M

接下来N-1行,每行两个整数表示树边连接的点

再接下来M行,每行两个数u,v表示询问

输出格式:

对于每个询问,输出一行一个整数表示答案

解法

(像这种树剖和LCT没法维护的,果然还是用莫队好了=w=)

树分块呢,大概有这样的一些方法:

王室联邦分块法:可以保证每个块的大小和直径都不超过2N−−√−1,但是不保证块联通

DFS序分块法:首先是好写(毕竟转化成了序列问题),严格保证块大小N−−√,但是不保证直径,也不保证联通。处理子树信息比较方便

size分块:检查当前节点的父亲所在块的大小,如果小于N−−√就把当前节点加入进去,不然新开块。块大小最坏N−−√,保证块内联通,还保证直径,多么优美啊=w=,可惜不能保证块个数(一个菊花图就死了恍恍惚惚哈哈哈哈)….

于是这道题呢,采用王室联邦分块法

维护的是两个端点到根的路径上的 贡献的异或。于是假设当前点为nowu,nowv,那么当前就应该维护下了nowu到nowv路径上的答案(不包含LCA,因为LCA会被异或两遍)。在查答案的时候单独加上LCA的贡献就可以了

关于修改,假设下一个询问的点为aimu,aimv,那么就把nowu挪动到aimu去,nowv挪动到aimv去,这一步可以通过暴力跳它们的父亲来实现(因为每个块大小直径都有保证,所以总的复杂度也是可以保证的)。

下面是自带大常数的代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M , tp , head[40005] ;
int dfn[40005] , dfs_c , val[40005] ;
int uninum , bel[40005] , Bsiz , Btot , ans[100005] ;

struct Unique_Data{
int num , id ;
bool operator < ( const Unique_Data &A ) const {
return num < A.num ;
}
}Uni[40005] ;

struct Queries{
int u , v , id ;
bool operator < ( const Queries &A ) const {
return ( bel[u] < bel[A.u] ) ||
( bel[u] == bel[A.u] && dfn[v] < dfn[A.v] ) ;
}
}Q[100005] ;

struct Path{
int pre , to ;
}p[80005] ;

void In( int t1 , int t2 ){
p[++tp].pre = head[t1] ;
p[ head[t1] = tp ].to = t2 ;
}

void Unique_(){
sort( Uni + 1 , Uni + N + 1 ) ;
for( int i = 1 , las = -2e9 ; i <= N ; i ++ ){
if( las != Uni[i].num ){
uninum ++ ;
las = Uni[i].num ;
}
val[ Uni[i].id ] = uninum ;
}
}

int dep[40005] , fa[16][40005] , sta[40005] , topp ;
void dfs( int u ){
dfn[u] = ++dfs_c ;
int last = topp ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( v == fa[0][u] ) continue ;
dep[v] = dep[u] + 1 ;
fa[0][v] = u ;
dfs( v ) ;
if( topp - last >= Bsiz ){
Btot ++ ;
while( topp != last ) bel[ sta[topp--] ] = Btot ;
}
}
sta[++topp] = u ;
}

void getST(){
for( int i = 1 ; i <= 15 ; i ++ )
for( int j = 1 ; j <= N ; j ++ )
fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
}

int Lca( int u , int v ){
if( dep[u] < dep[v] ) swap( u , v ) ;
int t = dep[u] - dep[v] , x = 0 ;
while( t ){
if( t&1 ) u = fa[x][u] ;
t >>= 1 ; x ++ ;
}
if( u == v ) return u ;
for( int i = 15 ; i >= 0 ; i -- )
if( fa[i][u] != fa[i][v] )
u = fa[i][u] , v = fa[i][v] ;
return fa[0][u] ;
}

void init(){
Unique_() ;
Bsiz = sqrt( N ) ;
dfs( 1 ) ; fa[0][1] = 1 ;
if( topp ){
Btot ++ ;
while( topp ) bel[ sta[topp--] ] = Btot ;
}
sort( Q + 1 , Q + M + 1 ) ;
getST() ;
}

bool vis[40005] ;
int cnt[40005] , nowans ;
void Xor( int x ){
if( vis[x] ){
vis[x] = false ;
if( -- cnt[ val[x] ] == 0 ) nowans -- ;
} else {
vis[x] = true ;
if( ++ cnt[ val[x] ] == 1 ) nowans ++ ;
}
}

void Move_( int u , int v ){
if( dep[u] < dep[v] ) swap( u , v ) ;
while( dep[u] > dep[v] ){
Xor( u ) ;
u = fa[0][u] ;
}
while( u != v ){
Xor( u ) ; Xor( v ) ;
u = fa[0][u] , v = fa[0][v] ;
}
}

void solve(){
int nowu = 1 , nowv = 1 ;
for( register int i = 1 ; i <= M ; i ++ ){
int aimu = Q[i].u , aimv = Q[i].v , LCA = Lca( aimu , aimv ) ;
Move_( nowu , aimu ) ; nowu = aimu ;
Move_( nowv , aimv ) ; nowv = aimv ;
Xor( LCA ) ;
ans[ Q[i].id ] = nowans ;
Xor( LCA ) ;
}
for( register int i = 1 ; i <= M ; i ++ )
printf( "%d\n" , ans[i] ) ;
}

inline void read_( int &x ){
int flag = 1 ; x = 0 ;
char ch = getchar() ;
while( ch < '0' || ch > '9' ){ if( ch == '-' ) flag = -1 ; ch = getchar() ; }
while( ch >='0' && ch <='9' ) x = ( x << 1 ) + ( x << 3 ) + ch - '0' , ch = getchar() ;
x *= flag ;
}

int main(){
scanf( "%d%d" , &N , &M ) ;
for( register int i = 1 ; i <= N ; i ++ ){
read_( val[i] ) ;
Uni[i].num = val[i] , Uni[i].id = i ;
}
for( int i = 1 , u = 0 , v = 0 ; i < N ; i ++ ){
read_( u ) , read_( v ) ;
In( u , v ) ; In( v , u ) ;
}
for( register int i = 1 ; i <= M ; i ++ ){
read_( Q[i].u ) , read_( Q[i].v ) ;
Q[i].id = i ;
}
init() ;
solve() ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: