弱化版: [NOI2014] 起床困难综合症

求出每一位初始是 0/1 的结果

  • 若 0 的结果为 1 ,直接加上该位贡献
  • 若 1 的结果为 1 ,若该位填 1 不会超过最大值,那么填 1

注意到每位相互独立,可以压位进行计算


回到原问题,根据上述做法不难想到用 线段树+树剖 维护链上第 \(i\) 位 正序/逆序 操作后初始为 0/1 的结果,然后使用上述贪心。

合并左右区间大概是这样: \(fs[i][0][1]=fr[i][0][fl[i][0][1]]\)

这样做复杂度为 \(\mathcal O(nk \log^2 n)\),瓶颈在于合并时无法像上面一样压位。

但是可能的 \(fl\) 的取值只有 0/1 ,那么 \(0\)\(1\) 出我们可以表示为:

\[([fl[i][0][0]=0]\land[fr[i][0][0]=1]) \lor ([fl[i][0][0]=1] \land [fr[i][0][1]=1] \]

这种位运算的形式使得我们可以压位,同理可得到其它转移,这里就不赘述了。

复杂度 \(\mathcal O(n \log^2 n)\) , 挺有意思的

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define ull unsigned long long
#define pii pair< ull , int >
#define fi first
#define sc second
#define mp make_pair

const int MAXN = 1e5;
int n , m , k; pii val[ MAXN + 5 ];
vector< int > Graph[ MAXN + 5 ];

int fa[ MAXN + 5 ] , hs[ MAXN + 5 ] , dep[ MAXN + 5 ] , siz[ MAXN + 5 ];
void dfs1( int u , int fat ) {
	fa[ u ] = fat; dep[ u ] = dep[ fat ] + 1; siz[ u ] = 1;
	for( int v : Graph[ u ] ) if( v != fa[ u ] ) {
		dfs1( v , u ); siz[ u ] += siz[ v ];
		if( siz[ hs[ u ] ] < siz[ v ] ) hs[ u ] = v;
	}
}
int tim , dfn[ MAXN + 5 ] , rk[ MAXN + 5 ] , top[ MAXN + 5 ];
void dfs2( int u , int tp ) {
	dfn[ u ] = ++ tim , rk[ tim ] = u; top[ u ] = tp;
	if( hs[ u ] ) dfs2( hs[ u ] , tp );
	for( int v : Graph[ u ] ) if( v != fa[ u ] && v != hs[ u ] ) dfs2( v , v );
}

ull op( bool a , bool b , int op ) {
	if( op == 1 ) return a & b;
	if( op == 2 ) return a | b;
	if( op == 3 ) return a ^ b;
}
struct node {
	ull a[ 2 ][ 2 ];
	node operator + ( const node &o ) const {
		node r;
		r.a[ 0 ][ 0 ] = ( a[ 0 ][ 0 ] & o.a[ 0 ][ 1 ] ) | ( ( ~a[ 0 ][ 0 ] ) & o.a[ 0 ][ 0 ] );
		r.a[ 0 ][ 1 ] = ( a[ 0 ][ 1 ] & o.a[ 0 ][ 1 ] ) | ( ( ~a[ 0 ][ 1 ] ) & o.a[ 0 ][ 0 ] );
		r.a[ 1 ][ 0 ] = ( o.a[ 1 ][ 0 ] & a[ 1 ][ 1 ] ) | ( ( ~o.a[ 1 ][ 0 ] ) & a[ 1 ][ 0 ] );
		r.a[ 1 ][ 1 ] = ( o.a[ 1 ][ 1 ] & a[ 1 ][ 1 ] ) | ( ( ~o.a[ 1 ][ 1 ] ) & a[ 1 ][ 0 ] );
		return r;
	}
};
struct Segment_Tree {
	node Tree[ 4 * MAXN + 5 ];
	
	#define ls x << 1
	#define rs x << 1 | 1
	#define mid ( l + r >> 1 )
	void Build( int x , int l = 1 , int r = n ) {
		if( l == r ) {
			for( int i = 0 ; i < k ; i ++ ) {
				Tree[ x ].a[ 0 ][ 0 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 0 , val[ rk[ l ] ].sc ) << i;
				Tree[ x ].a[ 1 ][ 0 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 0 , val[ rk[ l ] ].sc ) << i;
				Tree[ x ].a[ 0 ][ 1 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 1 , val[ rk[ l ] ].sc ) << i;
				Tree[ x ].a[ 1 ][ 1 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 1 , val[ rk[ l ] ].sc ) << i;
			}
			return;
		}
		Build( ls , l , mid ); Build( rs , mid + 1 , r );
		Tree[ x ] = Tree[ ls ] + Tree[ rs ];
	}
	void Update( int x , int pos , int l = 1 , int r = n ) {
		if( pos < l || pos > r ) return;
		if( l == r ) {
			Tree[ x ].a[ 0 ][ 0 ] = Tree[ x ].a[ 1 ][ 0 ] = Tree[ x ].a[ 0 ][ 1 ] = Tree[ x ].a[ 1 ][ 1 ] = 0;
			for( int i = 0 ; i < k ; i ++ ) {
				Tree[ x ].a[ 0 ][ 0 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 0 , val[ rk[ l ] ].sc ) << i;
				Tree[ x ].a[ 1 ][ 0 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 0 , val[ rk[ l ] ].sc ) << i;
				Tree[ x ].a[ 0 ][ 1 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 1 , val[ rk[ l ] ].sc ) << i;
				Tree[ x ].a[ 1 ][ 1 ] |= op( ( val[ rk[ l ] ].fi >> i ) & 1 , 1 , val[ rk[ l ] ].sc ) << i;
			}
			return;
		}
		Update( ls , pos , l , mid ); Update( rs , pos , mid + 1 , r );
		Tree[ x ] = Tree[ ls ] + Tree[ rs ];
	}
	node Query( int x , int ql , int qr , int l = 1 , int r = n ) {
		if( ql <= l && r <= qr ) return Tree[ x ];
		if( qr <= mid ) return Query( ls , ql , qr , l , mid );
		if( ql > mid ) return Query( rs , ql , qr , mid + 1 , r );
		return Query( ls , ql , qr , l , mid ) + Query( rs , ql , qr , mid + 1 , r );
	}
}Tr;
ull Query( int u , int v , ull mxw ) {
	node l , r , s; bool lf = 0 , rf = 0;
	while( top[ u ] != top[ v ] ) {
		if( dep[ top[ u ] ] < dep[ top[ v ] ] ) { //v
			node p = Tr.Query( 1 , dfn[ top[ v ] ] , dfn[ v ] );
			if( !rf ) r = p , rf = 1; else r = p + r;
			v = fa[ top[ v ] ];
		}
		else { //u
			node p = Tr.Query( 1 , dfn[ top[ u ] ] , dfn[ u ] );
			if( !lf ) l = p , lf = 1; else l = p + l;
			u = fa[ top[ u ] ];
		}
	}
	if( dep[ u ] < dep[ v ] ) {
		node p = Tr.Query( 1 , dfn[ u ] , dfn[ v ] );
		if( !rf ) r = p , rf = 1; else r = p + r;
	}
	else {
		node p = Tr.Query( 1 , dfn[ v ] , dfn[ u ] );
		if( !lf ) l = p , lf = 1; else l = p + l;
	}
	swap( l.a[ 0 ] , l.a[ 1 ] );
	if( lf && rf ) s = l + r;
	if( !lf ) s = r; if( !rf ) s = l;
	
	ull Ans = 0;
	for( int i = k - 1 ; i >= 0 ; i -- ) {
		if( ( s.a[ 0 ][ 0 ] >> i ) & 1 ) Ans += 1llu << i;
		else if( ( ( s.a[ 0 ][ 1 ] >> i ) & 1 ) && mxw >= ( 1llu << i ) ) { mxw -= 1llu << i , Ans += 1llu << i; }
	}
	return Ans;
}

int main( ) {
	scanf("%d %d %d",&n,&m,&k);
	for( int i = 1 ; i <= n ; i ++ ) scanf("%d %llu",&val[ i ].sc,&val[ i ].fi);
	for( int i = 1 , u , v ; i < n ; i ++ ) {
		scanf("%d %d",&u,&v);
		Graph[ u ].push_back( v );
		Graph[ v ].push_back( u );
	}
	dfs1( 1 , 0 ); dfs2( 1 , 1 ); Tr.Build( 1 );
	for( int i = 1 , op , x , y ; i <= m ; i ++ ) {
		ull z; scanf("%d %d %d %llu",&op,&x,&y,&z);
		if( op == 1 ) printf("%llu\n", Query( x , y , z ) );
		if( op == 2 ) val[ x ] = mp( z , y ) , Tr.Update( 1 , dfn[ x ] );
	}
	return 0;
}