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) ;
}