说在前面
昨天晚饭后和zyc在操场散步。
me说me今晚要A了决战那个水题,顺便再去写一两道数论水题
zyc表示me想多了,说me肯定会写一晚上决战
然而,前一天晚上只用了一个半小时就1A了首都的me,当然是一脸不服
然后?然后昨晚me果然没有切掉那道题…
一开始样例RE,静态查错找了一堆错出来
后来去看了别人的代码,又找了一些错误出来…然而还是没有过样例…直到昨晚离开机房仍然样例RE
到现在才发现me读入错了= =?改了就A了,内心绝望…
干死zyc!!! @Maxmercer
题目
BZOJ3159传送门
不是权限题,就不复制题面了
解法
这道题貌似民间解法有两个,LCT和树剖(官解貌似是LCT来着)
链加,链上询问最大最小和,LCT都可以轻松维护(把链提取出来,要询问的询问,修改就打标记)
关键是那个链权值反转操作。权值反转很容易想到要用Splay维护,但是并不能直接使用LCT里的那个Splay。因为LCT的Splay维护的是形态信息,如果直接在LCT的Splay里打翻转标记,不仅达不到翻转数值的效果,反而还把形态(深度)信息破坏了。
所以咱们再开一个Splay森林来维护权值,每棵形态Splay都对应一个权值Splay,在维护的时候只需要把形态Splay和权值Splay同步即可。链加,链询问最大最小和操作都在权值Splay上进行。Makeroot的翻转操作需要将两个Splay都翻转,而题目里的翻转操作只翻转权值Splay。这样就可以完美的维护所有的信息了
具体实现:
在形态Splay里存下对应的权值Splay的根(方便操作)。形态Splay里的fa指针不仅是Splay的信息,同时也是原树上的fa&&child信息。而权值Splay里的fa指针仅仅是Splay的信息,因此这里在Access的时候要特别注意,细节特别多,一定要想好再写。
然后就是,形态Splay和权值Splay可能并不长得一样,但是形态Splay里的第k个节点对应着Splay里的第k个节点,因此Access的时候需要用一个Find_Kth去找到对应节点。
还有,形态Splay里所有节点都要存下对应的权值Splay的根(也是为了方便写和维护),因此除了reverse标记,还应该有一个Node_flag标记。
其他细节应该还有不少,但是主要的大概就是这些了,这题很能练练码力的
如果你准备切掉这道题,或者正在debug,加油!!!
下面是自带大常数的代码
/**************************************************************
Problem: 3159
User: Izumihanako
Language: C++
Result: Accepted
Time:3212 ms
Memory:6096 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , M ;
struct Node{
bool isv , rev ;
long long sum ;
int minn , maxn , now , siz , addflag ;
Node *ch[2] , *fa , *valrt , *rtflag ;
void setrt( Node *nd ){
rtflag = valrt = nd ;
}
void rever(){
swap( ch[0] , ch[1] ) ;
rev ^= 1 ;
}
void add( const int &delta ){
minn += delta , maxn += delta ;
sum += 1LL * siz * delta , now += delta ;
addflag += delta ;
}
void update(){
siz = 1 ;
if( ch[0] ) siz += ch[0]->siz ;
if( ch[1] ) siz += ch[1]->siz ;
if( isv ){
minn = maxn = sum = now ;
if( ch[0] ){
minn = min( minn , ch[0]->minn ) ;
maxn = max( maxn , ch[0]->maxn ) ;
sum += ch[0]->sum ;
}
if( ch[1] ){
minn = min( minn , ch[1]->minn ) ;
maxn = max( maxn , ch[1]->maxn ) ;
sum += ch[1]->sum ;
}
}
}
void pushdown(){
if( rev ){
if( ch[0] ) ch[0]->rever() ;
if( ch[1] ) ch[1]->rever() ;
rev = false ;
}
if( addflag ){
if( ch[0] ) ch[0]->add( addflag ) ;
if( ch[1] ) ch[1]->add( addflag ) ;
addflag = 0 ;
}
if( rtflag ){
if( ch[0] ) ch[0]->setrt( rtflag ) ;
if( ch[1] ) ch[1]->setrt( rtflag ) ;
rtflag = NULL ;
}
}
}w [50005] , val[50005] ;
void init(){
for( int i = 1 ; i <= N ; i ++ ){
val[i].isv = true ;
w[i].valrt = &val[i] ;
w[i].siz = val[i].siz = 1 ;
}
}
bool isroot( Node *x ){
if( !x->fa ) return true ;
if( x->fa->ch[0] != x && x->fa->ch[1] != x ) return true ;
return false ;
}
void Rotate( Node *nd , bool aim ){
Node *x = nd->ch[aim] ;
if( nd->fa ){
if( nd->fa->ch[0] == nd ) nd->fa->ch[0] = x ;
else if( nd->fa->ch[1] == nd ) nd->fa->ch[1] = x ;
} x->fa = nd->fa ;
if( x->ch[aim^1] ){
x->ch[aim^1]->fa = nd ;
} nd->ch[aim] = x->ch[aim^1] ;
x->ch[aim^1] = nd , nd->fa = x ;
nd->update() ;
x->update() ;
}
int topp ;
Node *sta[50005] ;
void Splay( Node *nd ){
Node *tmp = nd ;
for( ; !isroot( tmp ) ; tmp = tmp->fa )
sta[++topp] = tmp ;
sta[++topp] = tmp ;
for( ; topp ; topp -- ) sta[topp]->pushdown() ;
while( !isroot( nd ) ){
Node *fa = nd->fa , *gdfa = fa->fa ;
bool pn = ( fa->ch[1] == nd ) , pf ;
if( gdfa && !isroot( fa ) ){
pf = ( gdfa->ch[1] == fa ) ;
if( pn == pf ){
Rotate( gdfa , pf ) ;
Rotate( fa , pn ) ;
} else {
Rotate( fa , pn ) ;
Rotate( gdfa , pf ) ;
}
} else Rotate( fa , pn ) ;
}
}
Node *Kth( Node *x , int K ){
x->pushdown() ;
int Lsiz = ( x->ch[0] ? x->ch[0]->siz : 0 ) + 1 ;
if( K == Lsiz ) return x ;
if( K < Lsiz ) return Kth( x->ch[0] , K ) ;
else return Kth( x->ch[1] , K - Lsiz ) ;
}
void Access( Node *nd ){
Node *tmp = NULL , *valnd ;
while( nd ){
Splay( nd ) ; Splay( nd->valrt ) ;
int rk = ( nd->ch[0] ? nd->ch[0]->siz : 0 ) + 1 ;
valnd = Kth( nd->valrt , rk ) ;//提取值树节点
Splay( valnd ) ;
if( valnd->ch[1] ){// Split Right child
valnd->ch[1]->fa = NULL ;
nd->ch[1]->setrt( valnd->ch[1] ) ;
}
if( tmp ){//Merge Right child
tmp->valrt->fa = valnd ;
valnd->ch[1] = tmp->valrt ;
} else valnd->ch[1] = NULL ;
valnd->update() ;
nd->ch[1] = tmp ;
nd->setrt( valnd ) ;//保证位置树的root始终映射到值域Splay的根节点
nd->update() ;
tmp = nd ;
nd = nd->fa ;
}
}
void Makeroot( Node *x ){
Access( x ) ;//只Access位置树 (实际上也只能Access位置树)
Splay( x ) ; Splay( x->valrt ) ;
x->rever() ; x->valrt->rever() ;
}
void Link( Node *x , Node *y ){
Makeroot( x ) ;
x->fa = y ;
}
void solve(){
char opt[15] ;
for( int i = 1 , xid , yid , ww ; i <= M ; i ++ ){
scanf( "%s%d%d" , opt , &xid , &yid ) ;
Node *x = ( w + xid ) , *y = ( w + yid ) ;
Makeroot( y ) ; Access( x ) ;
Splay( x ) ; Splay( x->valrt ) ;// Update Links
if( opt[0] == 'I' ){
if( opt[2] == 'c' ){// Increase
scanf( "%d" , &ww ) ;
x->valrt->add( ww ) ;
} else
x->valrt->rever() ;// Invert
} else if( opt[0] == 'M' ){
if( opt[1] == 'i' ) // Min
printf( "%d\n" , x->valrt->minn ) ;
else // Max
printf( "%d\n" , x->valrt->maxn ) ;
} else // Sum
printf( "%lld\n" , x->valrt->sum ) ;
}
}
int main(){
scanf( "%d%d%*d" , &N , &M ) ;
init() ;
for( int i = 1 , u , v ; i < N ; i ++ ){
scanf( "%d%d" , &u , &v ) ;
Link( w + u , w + v ) ;
}
solve() ;
}