模板
先吐嘈一下,这真的不是模板
有两个做法待填坑
\(splay\)做法\(*\)
启发式合并做法\(*\)
本文主要写线段树合并做法。
考场上写了一个树上差分,然后以为\(30pts\)稳了,结果分一出快乐爆0,因为我**的把所有的询问都存起来了,但是没有用到这些存起来的询问,然后到后面没有注意他这个询问顺序不一定从\(1\)到\(n\),然后我就以为他从\(1\)到\(n\),然后就。。。
树上差分之后\(dfs\)一遍解决,跟暴力跳祖先一个分,意料之中,必竟没什么思维含量。
但是我有一个地方智障了,没看到\(70pts\)的桶一直装不满这个数据范围,其实那个数据范围直接写一个线段树合并就有了。
70pts 分情况code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define int long
#define R register int
#define printf Ruusupuu = printf
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 1e5 + 10 ;
const int M = 1e3 + 10 ;
int Ruusupuu ;
int n , m , k , cnt , head [N] , t [N] , q ;
int st [M][M] , top [N] , x , y ; bool fg [N] ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
struct E{ int fr , to , next ; } a [N << 1] ;
struct O{ int ti , cor , rk , index ; } b [N] ;
inline void add( int f , int t ){
a [++ cnt].fr = f ;
a [cnt].to = t ;
a [cnt].next = head [f] ;
head [f] = cnt ;
}
namespace fris{
void sc(){
for( R i = 1 ; i <= n ; i ++ ) t [i] = read() ;
m = read() ; for( R i = 1 ; i <= m ; i ++ ) b [i].ti = read() , b [i].cor = read() , st [b [i].ti][++ top[b [i].ti]] = i ;
}
void dfs( int x , int fa ){
for( R i = head [x] ; ~i ; i = a [i].next ){
int y = a [i].to ; if( y == fa ) continue ;
dfs( y , x ) ;
for( R j = 1 ; j <= top [y] ; j ++ )
st [x][++ top[x]] = st [y][j] ;
}
}
void work(){
dfs( 1 , 0 ) ; q = read() ;
while( q -- ){
int i = read() ; sort( st [i] + 1 , st [i] + 1 + top [i] ) ;
memset( fg , 0 , sizeof( fg ) ) ; int cnt = 0 ;
for( R j = 1 ; j <= t [i] && j <= top [i] ; j ++ ){
if( ! fg [b [st [i][j]].cor] ) fg [b [st [i][j]].cor] = 1 , cnt ++ ;
} printf( "%ld\n" , cnt ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}
}
namespace sce{
int knt , gnt , root [N] , lnt , ans [N] , x ;
struct T{ int data , l , r , ls , rs ; } t [N << 6] ;
inline void ud( int x ){ t [x].data = ( t [t [x].ls].data + t [t [x].rs].data ) ; }
inline void ins( int &x , int l , int r , int pos , int delta ){
if( !x ) x = ++ gnt , t [x].l = l , t [x].r = r ;
if( l == r ){ t [x].data |= delta ; return ; }
int mid = ( l + r ) >> 1 ;
if( pos <= mid ) ins( t [x].ls , l , mid , pos , delta ) ;
else ins( t [x].rs , mid + 1 , r , pos , delta ) ;
ud ( x ) ;
}
inline int mer( int x , int y ){
if( !x ) return y ; if( !y ) return x ;
if( t [x].l == t [x].r ){ t [x].data |= t [y].data ; return x ; }
t [x].ls = mer( t [x].ls , t [y].ls ) ;
t [x].rs = mer( t [x].rs , t [y].rs ) ;
ud( x ) ; return x ;
}
inline bool cmp( O a , O b ){ return a.cor < b.cor ; }
inline bool csp( O a , O b ){ return a.index < b.index ; }
void sc(){
for( R i = 1 ; i <= n ; i ++ ) read() ;
m = read() ;
for( R i = 1 ; i <= m ; i ++ )
b [i].ti = read() , b [i].cor = read() , b [i].index = i ;
sort( b + 1 , b + 1 + m , cmp ) ;
for( R i = 1 ; i <= m ; i ++ )
if( b [i].cor == b [i - 1].cor ) b [i].rk = knt ;
else b [i].rk = ++ knt ;
sort( b + 1 , b + 1 + m , csp ) ;
for( R i = 1 ; i <= m ; i ++ )
ins( root [b [i].ti] , 1 , knt , b [i].rk , 1 ) ;
}
inline void dfs( int x , int fa ){
for( R i = head [x] ; ~i ; i = a [i].next ){
int y = a [i].to ; if( y == fa ) continue ;
dfs( y , x ) ;
root [x] = mer( root [x] , root [y] ) ;
} ans [x] = t [root [x]].data ;
}
void work(){
dfs( 1 , 0 ) ; q = read() ;
while( q -- ){
x = read() ;
printf( "%ld\n" , ans [x] ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}
}
void Sc(){
n = read() ; memset( head , -1 , sizeof( head ) ) ;
for( R i = 1 ; i < n ; i ++ ) x = read() , y = read() , add( x , y ) , add( y , x );
}
signed main(){
Sc() ;
if( n <= (int) 1e3 ){ return fris::main() ; }
else { return sce::main() ; }
return 0 ;
}
接着说正解,我活生生调了\(10hours\)。
不得不说线段树合并的题既好调又不好调,好多细节,一不注意就挂了。
这道题最难受的地方就在于他装球的顺序,所以我们只能对每一个节点开一个以时间为下标的权值线段树。
还是树上差分的思想,先\(ins\)在\(dfs\)。
如果我们之开一个以时间为下标的权值线段树,用一个变量来记录颜色的话,会发现如下问题
区间信息没有办法合并,除非你对每个节点开一个\(vector\),但是这样代价过大。
统计答案花费时间太多,由于没有办法合并信息,统计的时候只能遍历
这样相当与我们只利用了线段树利于区间操作和合并均摊时间保证的优势,并没有利用到极致(但不代表这样的线段树没用,下文读者就会看到如何这样应用线段树)
既然想让信息变得可以合并,我们首先就要更改我们的信息,记录每一个节点的信息肯定是不可行的。
我们可以统计这个区间内有多少小球,并且同时统计这些小球对答案造成多少贡献(之前有就是\(0\),之前没有就是\(1\))
为什么要这样建立信息?考虑什么信息可以合并到区间中并且可以让我们统计答案,就不难想到。
有了这两个信息,我们在\(dfs\)遍历完一个节点的所有子节点并将信息合并后,可以利用线段树上二分或者写一个搜索函数来实现找一段范围内有多少对答案有贡献的小球。
容易发现这段区间肯定是\([1,tg[x]]\),\(tg[x]\)代表\(x\)桶的最大容量。
我们再来想一想如何来维护这个信息。
再对每个节点开一棵以颜色为下标的权值线段树,维护这个区间内每种颜色第一次在哪里出现。
插入的时候注意同一个点同一个颜色对答案的贡献只能计算一次,小球总数要一直计算(别想某个智障一样都只计算一次)
合并的时候看每种颜色,如果出现两次,在树中对后出现的那个把对答案的贡献搞成\(0\)
思考片刻,不难发现,这棵颜色线段树并没有合并信息的必要性。虽然只用了其保证时空代价的性质,但已经足够,因为我们并不需要统计他的信息。
具体实现在下文,不过推荐自己思考如何实现。
以后不能思路还不清晰就开码了,码之前多想些细节,或者码的时候用点心,写不出来本质还是太菜
code
#include <cstring>
#include <algorithm>
#include <cstdio>
#define mp make_pair
#define R register int
#define int long
#define printf Ruusupuu = printf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int S = 1e5 + 10 ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline void wap( int &a , int &b ){ a = a ^ b , b = a ^ b , a = a ^ b ; }
inline int mins( int a , int b ){ int zt = b - a ; return a + ( zt & ( zt >> 31 ) ) ; }
inline int maxs( int a , int b ){ int zt = b - a ; return b - ( zt & ( zt >> 31 ) ) ; }
inline int abss( int a ){ int zt = a >> 31 ; return ( a + zt ) ^ zt ; }
int n , m , q , ans [S] , x , y ;
int cnt , knt , gnt , lnt , root [S] , rot [S] , head [S] , tg [S] ;
bool fg = 1 ;
struct C{ int data , ls , rs , l , r , cnt ; } z [S << 6] ;
struct I{ int index , rk , cor , node ; } b [S] ;
struct N{ int cans , csum , ls , rs , l , r ; } t [S << 6] ;
struct E{ int fr , to , next ; } a [S << 1] ;
inline void add( int f , int t ){
a [++ cnt].fr = f ;
a [cnt].to = t ;
a [cnt].next = head [f] ;
head [f] = cnt ;
}
inline bool cmp( I a , I b ){ return a.cor < b.cor ; }
inline bool csp( I a , I b ){ return a.index < b.index ; }
inline void ud( int x ){ t [x].cans = t [t [x].ls].cans + t [t [x].rs].cans , t [x].csum = t [t [x].ls].csum + t [t [x].rs].csum ; }
inline void inscolor( int &x , int l , int r , int pos , int data ){
if( !x ) x = ++ gnt , z [x].l = l , z [x].r = r ;
if( l == r ){ if( z [x].data ) fg = 0 ; else z [x].data = data ; z [x].cnt ++ ; return ; }
int mid = ( l + r ) >> 1 ;
if( pos <= mid ) inscolor( z [x].ls , l , mid , pos , data ) ;
else inscolor( z [x].rs , mid + 1 , r , pos , data ) ;
}
inline void instime( int &x , int l , int r , int pos , int data1 , int data2 ){
if( !x ) x = ++ lnt , t [x].l = l , t [x].r = r ;
if( l == r ){ t [x].cans += data1 , t [x].csum += data2 ; return ; }
int mid = ( l + r ) >> 1 ;
if( pos <= mid ) instime( t [x].ls , l , mid , pos , data1 , data2 ) ;
else instime( t [x].rs , mid + 1 , r , pos , data1 , data2 ) ;
ud( x ) ;
}
inline int mertime( int x , int y ){
if( !x ) return y ; if( !y ) return x ;
if( t [x].l == t [x].r ){ t [x].cans += t [y].cans , t [x].csum += t [y].csum ; return x ; }
t [x].ls = mertime( t [x].ls , t [y].ls ) ;
t [x].rs = mertime( t [x].rs , t [y].rs ) ;
ud( x ) ; return x ;
}
inline int mercolor( int x , int y , int prx ){
if( !x ) return y ; if( !y ) return x ;
if( z [x].l == z [x].r ){
instime( root [prx] , 1 , m , max( z [x].data , z [y].data ) , -1 , 0 ) ;
z [x].data = min( z [x].data , z [y].data ) ; return x ;
}
z [x].ls = mercolor( z [x].ls , z [y].ls , prx ) ;
z [x].rs = mercolor( z [x].rs , z [y].rs , prx ) ;
return x ;
}
inline int fiask( int x , int sum ){
if( sum == 0 ) return 0 ;
// printf( "%ld %ld %ld %ld\n" , x , t [x].l , t [x].r , sum ) ;
if( t [x].l == t [x].r ) return t [x].cans ;
if( t [t [x].ls].csum <= sum ) return t [t [x].ls].cans + fiask( t [x].rs , sum - t [t [x].ls].csum ) ;
else return fiask( t [x].ls , sum ) ;
}
inline void debug(){
for( R i = 1 ; i <= n ; i ++ ) printf( "%ld " , rot [i] ) ; puts( "" ) ;
for( R i = 1 ; i <= gnt ; i ++ ) printf( "COR%ld %ld %ld %ld %ld %ld\n" , i , z [i].data , z [i].l , z [i].r , z [i].ls , z [i].rs ) ;
for( R i = 1 ; i <= n ; i ++ ) printf( "%ld " , root [i] ) ; puts( "" ) ;
for( R i = 1 ; i <= lnt ; i ++ ) printf( "TIM%ld %ld %ld %ld %ld %ld %ld\n" , i , t [i].cans , t [i].csum , t [i].l , t [i].r , t [i].ls , t [i].rs ) ;
}
void sc(){
n = read() ; memset( head , -1 , sizeof( head ) ) ;
for( R i = 1 ; i < n ; i ++ ) x = read() , y = read() , add( x , y ) , add( y , x ) ;
for( R i = 1 ; i <= n ; i ++ ) tg [i] = read() ;
m = read() ;
for( R i = 1 ; i <= m ; i ++ ) b [i].index = i , b [i].node = read() , b [i].cor = read() ;
sort( b + 1 , b + 1 + m , cmp ) ; b [0].cor = -0x7fffffff ;
for( R i = 1 ; i <= m ; i ++ )
if( b [i].cor == b [i - 1].cor ) b [i].rk = knt ;
else b [i].rk = ++ knt ;
sort( b + 1 , b + 1 + m , csp ) ;
for( R i = 1 ; i <= m ; i ++ ){
fg = 1 ;
inscolor( rot [b [i].node] , 1 , knt , b [i].rk , i ) ;
instime( root [b [i].node] , 1 , m , i , fg , 1 ) ;
} //debug() ;
}
inline void dfs( int x , int fa ){
for( R i = head [x] ; ~i ; i = a [i].next ){
int y = a [i].to ; if( y == fa ) continue ;
dfs( y , x ) ;
root [x] = mertime( root [x] , root [y] ) ;
rot [x] = mercolor( rot [x] , rot [y] , x ) ;
} ans [x] = fiask( root [x] , tg [x] ) ;
}
void work(){
dfs( 1 , 0 ) ; // debug() ;
q = read() ;
while( q -- ){
x = read() ;
printf( "%ld\n" , ans [x] ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}

浙公网安备 33010602011771号