说在前面
这题的代码真是恶心到me了…
题目
题目大意
给出一棵有N个节点的树。再给出一个路径集合,集合里的元素是一个三元组(u,v,val),表示这是一条从u到v的路径,定义该条路径为val。现在给出一些询问形如(u,v,k),表示询问集合中路径是u到v的子路径,且权值第k小的元素的权值。
节点数,集合大小,询问数均不超过40000
输入输出格式
输入格式:
第一行三个整数N,P,Q,表示节点数,集合大小和询问个数
接下来N-1行,每行一个二元组(u,v),描述一条树边
再接下来P行,每行一个三元组(u,v,val)描述一个集合元素,含义如题
再接下来Q行,每行一个三元组(u,v,k)描述一个询问,含义如题
输出格式:
对于每个询问,输出一行一个数字,表示答案
解法
这道题用到了一个很妙的处理方式,把路径的包含问题转化了
根据题意,给出一条路径P,要求出集合中是P的子路径,且权值第k小的那个。正向找出符合条件的子路径是很麻烦的,于是倒着来,看集合里哪些元素能对该答案造成贡献。
假设子路径为(u,v),P为(x,y),设in[u],out[u]分别表示u的dfs序。
有两种情况,分类讨论一下:
- 当u是v的祖先时,设w是u的一个儿子,且v在w的子树里。此时需要一个点在v的子树里,另一个点在w的子树外,这样的话路径(x,y)就包含了路径(u,v)。写成不等式的形式:1≤in[x]≤in[w]-1 && in[v] ≤ in[y] ≤ out[v] 或者 in[v] ≤ in[y] ≤ out[v] && out[w]+1 ≤ in[x] ≤ N。示意图如下:
- 当u不是v的祖先时,此时需要一个点在u的子树里,一个点在v的子树里。写成不等式的形式:in[u] ≤ in[x] ≤ out[u] && in[v] ≤ in[y] ≤ out[v]。示意图就不画了,比较简单。
发现两种情况实际上都是二维区间第k大,于是可以使用树套树解决。
当然有一种更简单恶心的写法,就是把k大转判定问题,使用扫描线+二分答案完成此题,常数应该会小一些
下面是自带大长度的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , P , Q , tp , head[40005] , fa[16][40005] , lcnt , maxv , minv = 1e9 , ans[40005] ;
struct Path{
int pre , to ;
}p[80005] ;
struct options{
int x , y1 , y2 , val , delta ;
bool operator < ( const options &A ) const {
return x < A.x ;
}
}o[160005] , Lo[160005] , Ro[160005] ;
struct Queries{
int x , y , K , id ;
bool operator < ( const Queries &A ) const {
return x < A.x ;
}
Queries operator - ( const int emm ) const {
return ( Queries ){ x , y , K - emm , id } ;
}
}q[40005] , Lq[40005] , Rq[40005] ;
struct BIT{
int b[40005] ;
void Modify( int x , int delta ){
for( ; x <= N ; x += x&-x )
b[x] += delta ;
}
void Modify( int x , int y , int delta ){
Modify( y + 1 , -delta ) ; Modify( x , delta ) ;
}
int Query( int x ){
int rt = 0 ;
for( ; x ; x -= x&-x )
rt += b[x] ;
return rt ;
}
} B ;
void In( int t1 , int t2 ){
p[++tp].pre = head[t1] ;
p[ head[t1] = tp ].to = t2 ;
}
void setMat( int x1 , int x2 , int y1 , int y2 , int val ){
o[++lcnt] = ( options ){ x1 , y1 , y2 , val , 1 } ;
o[++lcnt] = ( options ){ x2 + 1 , y1 , y2 , val , -1 } ;
}
int dep[40005] , in[40005] , out[40005] , dfs_c ;
void dfs( int u ){
in[u] = ++dfs_c ;
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 ) ;
} out[u] = dfs_c ;
}
int getNxt( int u , int v ){
for( int i = 15 ; i >= 0 ; i -- )
if( dep[ fa[i][v] ] > dep[u] ) v = fa[i][v] ;
return v ;
}
void preWork(){
fa[0][1] = 1 ; dfs( 1 ) ;
for( int i = 1 ; i <= 15 ; i ++ )
for( int j = 1 ; j <= N ; j ++ )
fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
for( int i = 1 , u , v , val ; i <= P ; i ++ ){
scanf( "%d%d%d" , &u , &v , &val ) ;
minv = min( minv , val ) , maxv = max( maxv , val ) ;
if( in[u] > in[v] ) swap( u , v ) ;
if( in[u] < in[v] && out[v] <= out[u] ){
int w = getNxt( u , v ) ;
setMat( 1 , in[w] - 1 , in[v] , out[v] , val ) ;
if( out[w] < N ) setMat( in[v] , out[v] , out[w] + 1 , N , val ) ;
} else setMat( in[u] , out[u] , in[v] , out[v] , val ) ;
} sort( o + 1 , o + lcnt + 1 ) ;
for( int i = 1 , u , v , k ; i <= Q ; i ++ ){
scanf( "%d%d%d" , &u , &v , &k ) ;
if( in[u] > in[v] ) swap( u , v ) ;
q[i] = ( Queries ){ in[u] , in[v] , k , i } ;
} sort( q + 1 , q + Q + 1 ) ;
}
void CDQ( int opL , int opR , int qL , int qR , int vL , int vR ){
if( vL == vR ){
for( int i = qL ; i <= qR ; i ++ )
ans[ q[i].id ] = vL ;
return ;
}
int mid = ( vL + vR ) >> 1 , opLt = 0 , opRt = 0 , qLt = 0 , qRt = 0 , pt1 , pt2 ;
for( pt1 = opL , pt2 = qL ; pt2 <= qR ; pt2 ++ ){
while( pt1 <= opR && o[pt1].x <= q[pt2].x ){
if( o[pt1].val <= mid ){
Lo[++opLt] = o[pt1] ;
B.Modify( o[pt1].y1 , o[pt1].y2 , o[pt1].delta ) ;
} else Ro[++opRt] = o[pt1] ;
pt1 ++ ;
}
int tmp = B.Query( q[pt2].y ) ;
if( tmp >= q[pt2].K ) Lq[++qLt] = q[pt2] ;
else Rq[++qRt] = q[pt2] - tmp ;
}
for( int i = 1 ; i <= opLt ; i ++ )
B.Modify( Lo[i].y1 , Lo[i].y2 , -Lo[i].delta ) ;
while( pt1 <= opR ){
if( o[pt1].val <= mid ) Lo[++opLt] = o[pt1] ;
else Ro[++opRt] = o[pt1] ;
pt1 ++ ;
}
memcpy( o + opL , Lo + 1 , opLt * sizeof( options ) ) ;
memcpy( o + opL + opLt , Ro + 1 , opRt * sizeof( options ) ) ;
memcpy( q + qL , Lq + 1 , qLt * sizeof( Queries ) ) ;
memcpy( q + qL + qLt , Rq + 1 , qRt * sizeof( Queries ) ) ;
if( qLt ) CDQ( opL , opL + opLt - 1 , qL , qL + qLt - 1 , vL , mid ) ;
if( qRt ) CDQ( opR - opRt + 1 , opR , qR - qRt + 1 , qR , mid+1,vR ) ;
}
void solve(){
CDQ( 1 , lcnt , 1 , Q , minv , maxv ) ;
for( int i = 1 ; i <= Q ; i ++ )
printf( "%d\n" , ans[i] ) ;
}
int main(){
scanf( "%d%d%d" , &N , &P , &Q ) ;
for( int i = 1 , u , v ; i < N ; i ++ ){
scanf( "%d%d" , &u , &v ) ;
In( u , v ) ; In( v , u ) ;
}
preWork() ;
solve() ;
}