Description
你有一个坐标范围在
[
0
,
1
e
9
]
[0,1e9]
[ 0 , 1 e 9 ] 的网格图,设所有的满足
x
&
y
=
0
x\&y=0
x & y = 0 的点
(
x
,
y
)
(x,y)
( x , y ) 为好的点,易证好点形成了一棵树,我们以
(
0
,
0
)
(0,0)
( 0 , 0 ) 为根,给出
m
m
m 个好点点对
(
u
,
v
)
(u,v)
( u , v ) 并把它们之间的路径上的点染为黑色。 现在A和B在玩游戏,A先手,每一次可以选择一个黑点以及它的祖先链的一个可空子集,将它们反色,最后不能操作的输。 B现在想通过修改若干次使得自己必胜,每一次修改可以选择一个好点,并将它到根的路径反色。
n
≤
1
e
5
n\le1e5
n ≤ 1 e 5
Solution
首先由于每一个点
(
x
,
y
)
(x,y)
( x , y ) 只会向
(
x
−
1
,
y
)
,
(
x
,
y
−
1
)
(x-1,y),(x,y-1)
( x − 1 , y ) , ( x , y − 1 ) 中的一个连边,可以讨论最低不同的位置得到,因此这是一棵树。 把表打出来,很容易发现这是一个分型结构,任意两个点的
l
c
a
lca
l c a 或者它们的
d
f
n
dfn
d f n 可以从
(
0
,
0
)
(0,0)
( 0 , 0 ) 往下面跳
l
o
g
log
l o g 次求得。 考虑怎么玩这个游戏,这是一个平等博弈,可以考虑
S
G
SG
S G 函数,但是一开始我觉得一个黑点可以影响上面的黑点,因此不同的黑点应该是不独立的游戏才对。 实际上由于
S
G
SG
S G 是
x
o
r
xor
x o r 结合的,而操作又是反色,它们实际上是独立的游戏。 考虑将黑白色看作有多少个黑色,选择祖先链上某些点反色当作让黑色数+1,那么由于如果一个点上有偶数个黑色,它的
S
G
SG
S G 对于整体局面的
x
o
r
xor
x o r 和为0,相当于是消成了奇数,因此增加黑色数与原先的取反是等价的。 再简单推一推显然一个深度为
d
e
p
dep
d e p 的点
(
x
,
y
)
(x,y)
( x , y ) 有
S
G
=
2
d
e
p
=
2
x
+
y
SG=2^{dep}=2^{x+y}
S G = 2 d e p = 2 x + y . 操作次数相当于是最后
S
G
SG
S G 连续
1
1
1 的段的个数。 后面就比较套路了,可以直接建立一个虚树,染色即可,当然也不一定要建出来,由于有关的连续垂直或平行边只有
n
l
o
g
n
n\ log\ n
n l o g n 条,用类似的东西维护每一段边的染色段也可以。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define maxn 400005
using namespace std;
int m, i, j, k, tot, cnt;
struct node{ int x, y; node ( int _x= 0 , int _y= 0 ) { x= _x, y= _y; } } a[ maxn] [ 2 ] , p[ maxn] ;
int operator< ( node a, node b) { return a. x< b. x|| a. x== b. x&& a. y< b. y; }
int operator== ( node a, node b) { return a. x== b. x&& a. y== b. y; }
map< node, int > num; node pnum[ maxn] ;
map< int , int > sum;
map< int , int > : : iterator it;
int nm ( node a) {
if ( num. find ( a) == num. end ( ) )
num[ a] = ++ tot, pnum[ tot] = a;
return num[ a] ;
}
int _2[ 31 ] ;
int getb ( node a, int x, int y, int k) {
if ( a. x< x+ _2[ k] && a. y< y+ _2[ k] ) return 0 ;
if ( a. x>= x+ _2[ k] ) return 1 ;
return 2 ;
}
int cmp ( node a, node b) {
int x= 0 , y= 0 ;
for ( int k= 30 ; k>= 0 ; k-- ) {
int t1= getb ( a, x, y, k) , t2= getb ( b, x, y, k) ;
if ( t1== t2) {
if ( t1== 1 ) x+ = _2[ k] ;
if ( t1== 2 ) y+ = _2[ k] ;
} else {
if ( t1== 0 ) {
if ( t2== 1 ) return 1 ;
if ( t2== 2 ) return a. x== x;
}
if ( t2== 0 ) {
if ( t1== 1 ) return 0 ;
if ( t1== 2 ) return b. x!= x;
}
return t1> t2;
}
}
return 0 ;
}
node lca ( node a, node b) {
int x= 0 , y= 0 ;
for ( int k= 30 ; k>= 0 ; k-- ) {
int t1= getb ( a, x, y, k) , t2= getb ( b, x, y, k) ;
if ( t1== t2) {
if ( t1== 1 ) x+ = _2[ k] ;
if ( t1== 2 ) y+ = _2[ k] ;
} else {
if ( t1+ t2== 3 ) return node ( x, y) ;
if ( t2== 0 ) swap ( t1, t2) , swap ( a, b) ;
if ( t2== 1 ) b= node ( x+ _2[ k] - 1 , y) ;
if ( t2== 2 ) b= node ( x, y+ _2[ k] - 1 ) ;
}
}
return a;
}
int em, e[ maxn] , nx[ maxn] , ls[ maxn] , w, fa[ maxn] , c0[ maxn] , c1[ maxn] ;
node d[ maxn] ;
void insert ( int x, int y) {
em++ ; e[ em] = y; nx[ em] = ls[ x] ; ls[ x] = em;
fa[ y] = x;
}
void maketree ( ) {
d[ w= 1 ] = node ( 0 , 0 ) ;
for ( i= 1 ; i<= cnt; i++ ) {
node x= p[ i] ;
if ( x== d[ w] ) continue ;
node y= lca ( d[ w] , x) ;
if ( y== d[ w] ) d[ ++ w] = x; else {
while ( cmp ( y, d[ w- 1 ] ) )
insert ( nm ( d[ w- 1 ] ) , nm ( d[ w] ) ) , w-- ;
if ( y== d[ w- 1 ] ) {
insert ( nm ( d[ w- 1 ] ) , nm ( d[ w] ) ) ;
d[ w] = x;
} else {
insert ( nm ( y) , nm ( d[ w] ) ) ;
d[ w] = y, d[ ++ w] = x;
}
}
}
while ( w> 1 ) insert ( nm ( d[ w- 1 ] ) , nm ( d[ w] ) ) , w-- ;
}
void cover ( int l, int r) {
sum[ l] ^ = 1 , sum[ r+ 1 ] ^ = 1 ;
}
void dfs ( int x) {
for ( int i= ls[ x] ; i; i= nx[ i] )
dfs ( e[ i] ) , c0[ x] + = c0[ e[ i] ] ;
if ( c0[ x] )
cover ( pnum[ x] . x+ pnum[ x] . y, pnum[ x] . x+ pnum[ x] . y) ;
c0[ x] + = c1[ x] ;
if ( c0[ x] && fa[ x] )
cover ( pnum[ fa[ x] ] . x+ pnum[ fa[ x] ] . y+ 1 , pnum[ x] . x+ pnum[ x] . y- 1 ) ;
}
int main ( ) {
freopen ( "ceshi.in" , "r" , stdin ) ;
freopen ( "ceshi1.out" , "w" , stdout ) ;
for ( i= 0 ; i<= 30 ; i++ ) _2[ i] = 1 << i;
scanf ( "%d" , & m) ;
for ( i= 1 ; i<= m; i++ ) {
scanf ( "%d%d%d%d" , & a[ i] [ 0 ] . x, & a[ i] [ 0 ] . y, & a[ i] [ 1 ] . x, & a[ i] [ 1 ] . y) ;
p[ ++ cnt] = a[ i] [ 0 ] , p[ ++ cnt] = a[ i] [ 1 ] ;
}
sort ( p+ 1 , p+ 1 + cnt, cmp) ;
maketree ( ) ;
for ( i= 1 ; i<= m; i++ ) {
c0[ nm ( a[ i] [ 0 ] ) ] ++ , c0[ nm ( a[ i] [ 1 ] ) ] ++ ;
c1[ nm ( lca ( a[ i] [ 0 ] , a[ i] [ 1 ] ) ) ] - = 2 ;
}
dfs ( nm ( node ( 0 , 0 ) ) ) ;
int ans= 0 ;
for ( it= sum. begin ( ) ; it!= sum. end ( ) ; it++ )
ans+ = ( * it) . second;
ans= ans- sum[ 0 ] ;
printf ( "%d\n" , ans) ;
}