您的位置:首页 > 其它

bzoj 1095 括号序列求两点距离

2015-06-15 20:28 288 查看
大致题意: 给一棵树,每个节点最开始都是黑色,有两种操作,1.询问树中相距最远的一对黑点的距离 2.反转一个节点的颜色

一种做法:

  建立出树的括号序列,类似这样: [A[B][C]],所以长度为3*n

  假如我们要询问AC间的距离,提取出中间的括号:[]],匹配消去后得到],其长度就是距离.

  现在我们要做的就是修改点的状态,并且动态维护答案.要用到一些求与绝对值相关的式子的技巧.

/**************************************************************
Problem: 1095
User: idy002
Language: C++
Result: Accepted
Time:2176 ms
Memory:55548 kb
****************************************************************/

#include <cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define N 100010
#define M N<<1
#define oo 0x3f3f3f3f
#define fprintf(...)

struct Node {
int v[7], c, e, a;
int lf, rg;
Node *ls, *rs;
void init( int type ) {
if( type==0 ) {
a = 0;
e = false;
c = 1;
for( int t=0; t<7; t++ ) v[t]=0;
} else if( type==-1 ) { //  (1,0)
a = -1;
e = true;
c = 0;
v[1] = v[3] = v[6] = v[2] = v[5] = 1;
v[0] = v[4] = -1;
} else {                //  (0,1)
a = -1;
e = true;
c = 0;
v[1] = v[3] = v[6] = v[0] = v[4] = 1;
v[2] = v[5] = -1;
}
}
void update() {
e = ls->e && rs->e;
v[5] = ls->v[5] + rs->v[5];
v[4] = ls->v[4] + rs->v[4];
v[6] = max( ls->v[6]+rs->v[4], ls->v[5]+rs->v[6] );
if( !ls->e && !rs->e ) {
v[0] = max( ls->v[0], ls->v[4]+rs->v[0] );
v[1] = max( ls->v[1], max( ls->v[6]+rs->v[0], ls->v[5]+rs->v[1] ) );
v[2] = max( rs->v[2], ls->v[2]+rs->v[5] );
v[3] = max( rs->v[3], max( ls->v[3]+rs->v[4], ls->v[2]+rs->v[6] ) );
a = max( max(ls->v[3]+rs->v[0],ls->v[2]+rs->v[1]), max(ls->a,rs->a) );
} else if( !ls->e ) {
v[0] = ls->v[0];
v[1] = ls->v[1];
v[2] = ls->v[2]+rs->v[5];
v[3] = max( ls->v[3]+rs->v[4], ls->v[2]+rs->v[6] );
a = ls->a;
} else if( !rs->e ) {
v[0] = ls->v[4]+rs->v[0];
v[1] = max( ls->v[6]+rs->v[0], ls->v[5]+rs->v[1] );
v[2] = rs->v[2];
v[3] = rs->v[3];
a = rs->a;
} else {
a = -1;
}
}
void reverse( int pos ) {
if( lf==rg ) {
if( c ) {
c = 0;
a = -1;
e = true;
} else {
c = 1;
a = 0;
e = false;
}
return;
}
int mid=(lf+rg)>>1;
if( pos<=mid ) ls->reverse(pos);
else rs->reverse(pos);
update();
}
}pool[N*3*3], *tail=pool, *root;

int n, m;
int head
, dest[M], next[M], etot;
int dfn
, sgn[N*3], fat
, idc;

void adde( int u, int v ) {
etot++;
next[etot] = head[u];
dest[etot] = v;
head[u] = etot;
}
void dfs( int u ) {
sgn[++idc] = 1;
dfn[u] = ++idc;
fprintf( stderr, "[" );
fprintf( stderr, "%d", u );
for( int t=head[u]; t; t=next[t] ) {
int v=dest[t];
if( v==fat[u] ) continue;
fat[v] = u;
dfs(v);
}
sgn[++idc] = -1;
fprintf( stderr, "]" );
}
Node *build( int lf, int rg ) {
Node *nd = ++tail;
nd->lf=lf, nd->rg=rg;
if( lf==rg ) {
nd->init( sgn[lf] );
} else {
int mid=(lf+rg)>>1;
nd->ls = build( lf, mid );
nd->rs = build( mid+1, rg );
nd->update();
}
fprintf( stderr, "[%d,%d] a=%2d e=%d v[0~6] = %2d %2d %2d %2d %2d %2d %2d\n",
lf, rg, nd->a, nd->e, nd->v[0], nd->v[1],
nd->v[2], nd->v[3], nd->v[4], nd->v[5], nd->v[6] );
return nd;
}
int main() {
scanf( "%d", &n );
for( int i=1,u,v; i<n; i++ ) {
scanf( "%d%d", &u, &v );
adde( u, v );
adde( v, u );
}
for( int i=1; i<=n+n+n; i++ )
fprintf( stderr, "%d", i%10 );
fprintf( stderr, "\n" );
fat[1] = 0;
dfs(1);
fprintf( stderr, "\n" );
root = build( 1, idc );
scanf( "%d", &m );
for( int i=1,u; i<=m; i++ ) {
char ch[10];
scanf( "%s\n", ch );
if( ch[0]=='G' ) {
printf( "%d\n", root->a );
} else {
scanf( "%d", &u );
root->reverse(dfn[u]);
}
}
}


View Code

另一种做法大概是用点分,然后用堆维护最值.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: