传送门:【Codeforces】Codeforces Round #299 (Div. 1) E. Tavas on the Path
大概题意:
首先对于一个串s,我们可以提取m个只包含1的块,每个块都是s里的一个最长连续1子串。
然后我们设x1,x2,x3...xm分别为这m个块中1的长度。
现在我们有函数T(S),定义 ,f(xi)是f关于xi的函数,下面会给出。
现在我们有一棵树,N个节点N-1条边,每条边一个边权。然后我们有N-1个f(x)的值,分别对应不同的长度。
然后我们有Q次询问,每次询问包含两个点u,v和一个权值w,然后我们用这条路径上所有的边来构造一个01串b,其中边权大于等于w的赋值为1,否则赋值为0(比如询问是u=1,v=6,w=2,5条边分别是(1,2,1),(2,3,2),(3,4,1),(4,5,2),(5,6,2),那么构成的01串b就是01011)。然后我们就要输出T(b)的值,也就是f(1)+f(2)。
题目要求就是,给一棵N节点的树,N-1个f函数,N-1条边,Q个询问,每次询问输出T(b)的值。
题目分析:
这个如果看懂题意,并且会树链剖分以及区间合并操作的话,就没有问题了。。
首先!我们先对边以及操作离个线,然后按照权值排序(升序降序随意,我用的升序),把所有的边都设为1然后插入到线段树中(即一开始的路径都是全1串),然后按顺序枚举询问,将权值小于询问的边对应的值置为0。这样我们就可以离线处理问题啦~
接下来大致讲一下树链剖分中的区间合并怎么操作。
我们假设路径是有向的,那么我们维护左链,维护左链的右端点Lnum(Lnum=1当且仅当左链的右端点是1),以及从左链的右端往左的连续1个数Llen,对于右链我们同理,维护Rnum,Rlen。我们再维护一个答案ans,表示这次查询的结果。这些变量初始值都设为0。
线段树内的查询,我们可以维护7个值(为了方便。。),区间左端点lnum,右端点rnum,左端点向右的连续1个数llen,右端点向左的连续1个数rlen,区间内部分块的和sum,区间范围[l,r]。对于区间合并,我们设左区间为tmpl,右区间为tmpr,要合并成的区间为tmp,那么首先tmp.sum = tmpl.sum + tmpr.sum,毋庸置疑。然后什么时候需要改变sum?自然是tmpl.rnum和tmpr.lnum都等于1的时候了,这时候tmp.sum += f[tmpl.rlen + tmpr.llen] - f[tmpl.rlen] - f[tmpr.llen]。其他值的转移的应该就不用我细说了。处理完以后这个区间就返回一个tmp即可。这样线段树内的维护就没问题了。
然后对于路径上的所有子区间,我们发现这些子区间是从下到上依次访问的,所以我们挨个合并,并且分两路合并,对于左链上的子区间tmp,维护方式和线段树内的维护尤为相似(本就是同一种操作嘛~),就是看Lnum是不是和tmp.rnum一样都为1(我们假设子区间的llen靠近根,而rlen靠近叶子),是的话我们就维护ans,ans += f[Llen + tmp.rlen] - f[Llen] - f[tmp.rlen]。其他操作不细说了。
对于右链和左链类似的处理就行了。注意各个变量不要搞错!
最后到了左链和右链汇合的时候了,如果中间还有个区间,三个依次合并,否则直接合并左右链。然后对于一个操作我们就做完了~
因为是离线的,所以我们用一个数组保存解,最后依次输出即可。
my code:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;
typedef long long LL ;
typedef unsigned long long ULL ;
#define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define root 1 , 1 , n
#define rt o , l , r
#define mid ( ( l + r ) >> 1 )
const int MAXN = 100005 ;
const int MAXE = 200005 ;
struct Edge {
int v , n ;
Edge () {}
Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;
struct Node {
int x , y , z , idx ;
void input ( int i ) {
scanf ( "%d%d%d" , &x , &y , &z ) ;
idx = i ;
}
bool operator < ( const Node& a ) const {
return z < a.z ;
}
} ;
struct Query_Node {
int lnum , rnum , llen , rlen , sum , l , r ;
Query_Node () {}
Query_Node ( int lnum , int rnum , int llen , int rlen , int sum , int l , int r ) :
lnum ( lnum ) , rnum ( rnum ) , llen ( llen ) , rlen ( rlen ) , sum ( sum ) , l ( l ) , r ( r ) {}
} ;
Node e[MAXN] , q[MAXN] ;
Edge E[MAXE] ;
int H[MAXN] , cntE ;
int pre[MAXN] ;
int siz[MAXN] ;
int son[MAXN] ;
int dep[MAXN] ;
int pos[MAXN] ;
int idx[MAXN] ;
int top[MAXN] ;
int tree_idx ;
int n , m ;
int f[MAXN] ;
int sum[MAXN << 2] ;
int lnum[MAXN << 2] ;
int rnum[MAXN << 2] ;
int llen[MAXN << 2] ;
int rlen[MAXN << 2] ;
int res[MAXN] ;
void clear () {
tree_idx = 0 ;
dep[1] = 0 ;
pre[1] = 0 ;
siz[0] = 0 ;
cntE = 0 ;
clr ( H , -1 ) ;
}
void addedge ( int u , int v ) {
E[cntE] = Edge ( v , H[u] ) ;
H[u] = cntE ++ ;
}
void dfs ( int u ) {
siz[u] = 1 ;
son[u] = 0 ;
for ( int i = H[u] ; ~i ; i = E[i].n ) {
int v = E[i].v ;
if ( v == pre[u] ) continue ;
pre[v] = u ;
dep[v] = dep[u] + 1 ;
dfs ( v ) ;
siz[u] += siz[v] ;
if ( siz[son[u]] < siz[v] ) son[u] = v ;
}
}
void rebuild ( int u , int top_element ) {
top[u] = top_element ;
pos[u] = ++ tree_idx ;
if ( son[u] ) rebuild ( son[u] , top_element ) ;
for ( int i = H[u] ; ~i ; i = E[i].n ) {
int v = E[i].v ;
if ( v != pre[u] && v != son[u] ) rebuild ( v , v ) ;
}
}
void pushup ( int o , int l , int r ) {
sum[o] = sum[ls] + sum[rs] ;
llen[o] = llen[ls] ;
rlen[o] = rlen[rs] ;
lnum[o] = lnum[ls] ;
rnum[o] = rnum[rs] ;
if ( rnum[ls] && lnum[rs] ) {
sum[o] -= f[rlen[ls]] + f[llen[rs]] ;
sum[o] += f[rlen[ls] + llen[rs]] ;
}
int m = mid ;
if ( llen[ls] == m - l + 1 ) llen[o] += llen[rs] ;
if ( rlen[rs] == r - m ) rlen[o] += rlen[ls] ;
}
void update ( int x , int v , int o , int l , int r ) {
if ( l == r ) {
lnum[o] = rnum[o] = v ;
llen[o] = rlen[o] = v ;
if ( v ) sum[o] = f[1] ;
else sum[o] = 0 ;
return ;
}
int m = mid ;
if ( x <= m ) update ( x , v , lson ) ;
else update ( x , v , rson ) ;
pushup ( rt ) ;
}
Query_Node query ( int L , int R , int o , int l , int r ) {
if ( L <= l && r <= R ) return Query_Node ( lnum[o] , rnum[o] , llen[o] , rlen[o] , sum[o] , l , r ) ;
int m = mid ;
if ( R <= m ) return query ( L , R , lson ) ;
if ( m < L ) return query ( L , R , rson ) ;
Query_Node tmpl = query ( L , R , lson ) ;
Query_Node tmpr = query ( L , R , rson ) ;
Query_Node tmp = Query_Node ( tmpl.lnum , tmpr.rnum , tmpl.llen , tmpr.rlen , tmpl.sum + tmpr.sum , tmpl.l , tmpr.r ) ;
if ( tmpl.rnum && tmpr.lnum ) {
tmp.sum -= f[tmpl.rlen] + f[tmpr.llen] ;
tmp.sum += f[tmpl.rlen + tmpr.llen] ;
}
if ( tmpl.llen == tmpl.r - tmpl.l + 1 ) tmp.llen += tmpr.llen ;
if ( tmpr.rlen == tmpr.r - tmpr.l + 1 ) tmp.rlen += tmpl.rlen ;
return tmp ;
}
int Query ( int x , int y ) {
int ans = 0 ;
int Llen = 0 , Rlen = 0 , Lnum = 0 , Rnum = 0 ;
while ( top[x] != top[y] ) {
if ( dep[top[x]] > dep[top[y]] ) {
Query_Node tmp = query ( pos[top[x]] , pos[x] , root ) ;
ans += tmp.sum ;
if ( tmp.rnum && Lnum ) {
ans -= f[tmp.rlen] + f[Llen] ;
ans += f[tmp.rlen + Llen] ;
}
if ( tmp.llen == tmp.r - tmp.l + 1 ) Llen += tmp.llen ;
else Llen = tmp.llen ;
Lnum = tmp.lnum ;
x = pre[top[x]] ;
} else {
Query_Node tmp = query ( pos[top[y]] , pos[y] , root ) ;
ans += tmp.sum ;
if ( tmp.rnum && Rnum ) {
ans -= f[tmp.rlen] + f[Rlen] ;
ans += f[tmp.rlen + Rlen] ;
}
if ( tmp.llen == tmp.r - tmp.l + 1 ) Rlen += tmp.llen ;
else Rlen = tmp.llen ;
Rnum = tmp.lnum ;
y = pre[top[y]] ;
}
}
if ( dep[x] < dep[y] ) {
Query_Node tmp = query ( pos[x] + 1 , pos[y] , root ) ;
ans += tmp.sum ;
if ( Lnum && tmp.lnum ) {
ans -= f[Llen] + f[tmp.llen] ;
ans += f[Llen + tmp.llen] ;
}
if ( tmp.rlen == tmp.r - tmp.l + 1 ) {
Llen += tmp.rlen ;
if ( Rnum ) {
ans -= f[Llen] + f[Rlen] ;
ans += f[Llen + Rlen] ;
}
} else {
if ( tmp.rnum && Rnum ) {
ans -= f[tmp.rlen] + f[Rlen] ;
ans += f[tmp.rlen + Rlen] ;
}
}
} else if ( dep[y] < dep[x] ) {
Query_Node tmp = query ( pos[y] + 1 , pos[x] , root ) ;
ans += tmp.sum ;
if ( Lnum && tmp.rnum ) {
ans -= f[Llen] + f[tmp.rlen] ;
ans += f[Llen + tmp.rlen] ;
}
if ( tmp.llen == tmp.r - tmp.l + 1 ) {
Llen += tmp.rlen ;
if ( Rnum ) {
ans -= f[Llen] + f[Rlen] ;
ans += f[Llen + Rlen] ;
}
} else {
if ( tmp.lnum && Rnum ) {
ans -= f[tmp.llen] + f[Rlen] ;
ans += f[tmp.llen + Rlen] ;
}
}
} else {
if ( Lnum && Rnum ) {
ans -= f[Llen] + f[Rlen] ;
ans += f[Llen + Rlen] ;
}
}
return ans ;
}
void solve () {
clear () ;
rep ( i , 1 , n ) scanf ( "%d" , &f[i] ) ;
rep ( i , 1 , n ) {
e[i].input ( i ) ;
addedge ( e[i].x , e[i].y ) ;
addedge ( e[i].y , e[i].x ) ;
}
rep ( i , 0 , m ) q[i].input ( i ) ;
dfs ( 1 ) ;
rebuild ( 1 , 1 ) ;
sort ( e + 1 , e + n ) ;
sort ( q + 0 , q + m ) ;
clr ( sum , 0 ) ;
clr ( lnum , 0 ) ;
clr ( rnum , 0 ) ;
clr ( llen , 0 ) ;
clr ( rlen , 0 ) ;
rep ( i , 1 , n ) {
int u = e[i].x , v = e[i].y ;
if ( dep[u] > dep[v] ) idx[i] = u ;
else idx[i] = v ;
update ( pos[idx[i]] , 1 , root ) ;
}
int j = 1 ;
rep ( i , 0 , m ) {
while ( j < n && e[j].z < q[i].z ) {
update ( pos[idx[j]] , 0 , root ) ;
++ j ;
}
res[q[i].idx] = Query ( q[i].x , q[i].y ) ;
}
rep ( i , 0 , m ) printf ( "%d\n" , res[i] ) ;
}
int main () {
while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
return 0 ;
}