BZOJ3340 [CEOI2013] Tram

一个非常重要的观察:

也就是,对于一个点,我们一定会找一个距离最近的有点的行最远的位置,如果有多个,就找是否存在一个位置最近的有点的行只有一边有点,且这里可以和这一行错开,这样可以让距离变大一些。所以,两个点实际上的距离可以变成以行号差做第一关键字,列号差做第二关键字的距离。虽然大致思路是这样,但是其实这个东西有很多细节,如果用 set 维护会显得非常难写,于是考虑线段树维护。

我们对每一个线段树的节点维护出这个节点所代表的区间内的位置,表示用这个位置到左右最近的有点的行的距离为第一个关键字,是否可以错开作为第二关键字的大小的最大的位置,同时维护这个距离。如果区间内一个点也没有或者只有一个点,那么就不维护这两个东西了,直接设 $0$ 。

然后还需要维护最左边和最右边有点的行是谁,以及这行内有一个点(包括位置)还是两个点。

然后 pushup 是一个非常码的东西。

首先最左边和最右边点的维护是很显然的。否则,我们可以先尝试继承一下左右的最大距离。

然后考虑跨中点的情况,这个东西很需要画图推一下。大致思路是先分类讨论一下两边都是两列都有点,还是只有一边两列的有点,还是都不是两列都有点,再讨论一下长度的奇偶就行了。大致情况长这样:

image.png

然后,还需要特殊讨论一下放在开头结尾的情况。就拿线段树中开头结尾的点出来判一下即可。

用来练习代码能力算是一个好题。然后我写这题 pushup 也是重构了一次的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 1000006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m;

pii len[MAXN << 2] , ps[MAXN << 2] , lf[MAXN << 2] , rg[MAXN << 2];
// len : the maximum distance between two person, 1 if there is only one person. second: if it not at the same line
void pu( int rt ) {
len[rt] = ps[rt] = lf[rt] = rg[rt] = mp( 0 , 0 );
if( !lf[rt << 1].fi && !rg[rt << 1 | 1].fi ) return;
if( lf[rt << 1].fi ) lf[rt] = lf[rt << 1]; else lf[rt] = lf[rt << 1 | 1];
if( rg[rt << 1 | 1].fi ) rg[rt] = rg[rt << 1 | 1]; else rg[rt] = rg[rt << 1];
if( rg[rt << 1].fi && lf[rt << 1 | 1].fi ) {
pii L = rg[rt << 1] , R = lf[rt << 1 | 1];
if( L.se == 3 && R.se == 3 ) {
len[rt] = mp( ( R.fi - L.fi ) / 2 , 0 );
ps[rt] = mp( ( R.fi + L.fi ) / 2 , 1 );
} else if( L.se == 3 ) {
len[rt] = mp( ( R.fi - L.fi ) / 2 , ( R.fi + L.fi ) & 1 );
ps[rt] = mp( ( R.fi + L.fi ) / 2 + ( ( R.fi + L.fi ) & 1 ) , ( ( R.fi + L.fi ) & 1 ) ? ( R.se ^ 3 ) : 1 );
} else if( R.se == 3 ) {
len[rt] = mp( ( R.fi - L.fi ) / 2 , ( R.fi + L.fi ) & 1 );
ps[rt] = mp( ( R.fi + L.fi ) / 2 , ( ( R.fi + L.fi ) & 1 ) ? ( L.se ^ 3 ) : 1 );
} else {
len[rt] = mp( ( R.fi - L.fi ) / 2 , ( ( R.fi + L.fi ) & 1 ) || ( R.se == L.se ) );
ps[rt] = mp( ( R.fi + L.fi ) / 2 , ( R.fi + L.fi & 1 || R.se == L.se ) ? ( L.se ^ 3 ) : 1 );
}
}
if( len[rt << 1] >= len[rt] ) len[rt] = len[rt << 1] , ps[rt] = ps[rt << 1];
if( len[rt << 1 | 1] > len[rt] ) len[rt] = len[rt << 1 | 1] , ps[rt] = ps[rt << 1 | 1];
}
void ins( int rt , int l , int r , pii p ) {
if( l == r ) {
if( lf[rt].fi ) lf[rt].se = rg[rt].se = 3 , len[rt] = mp( 0 , 0 ) , ps[rt] = mp( 0 , 0 );
else lf[rt] = rg[rt] = p , len[rt] = mp( 1 , 0 ) , ps[rt] = mp( l , p.se ^ 3 );
return;
}
int m = l + r >> 1;
if( p.fi <= m ) ins( rt << 1 , l , m , p );
else ins( rt << 1 | 1 , m + 1 , r , p );
pu( rt );
}
void era( int rt , int l , int r , pii p ) {
if( l == r ) {
if( lf[rt].se == p.se ) lf[rt] = rg[rt] = len[rt] = ps[rt] = mp( 0 , 0 );
else {
// assert( ( lf[rt].se & p.se ) == p.se );
lf[rt].se -= p.se;
rg[rt].se = lf[rt].se;
ps[rt] = mp( l , lf[rt].se ^ 3 ) , len[rt] = mp( 1 , 0 );
}
return;
}
int m = l + r >> 1;
if( p.fi <= m ) era( rt << 1 , l , m , p );
else era( rt << 1 | 1 , m + 1 , r , p );
pu( rt );
}

pii lsj[MAXN];

void solve() {
cin >> n >> m;
char op[4]; int w;
rep( i , 1 , m ) {
scanf("%s",op);
if( op[0] == 'E' ) {
if( !lf[1].fi ) printf("%d %d\n",1,1) , lsj[i] = mp( 1 , 1 ) , ins( 1 , 1 , n , mp( 1 , 1 ) );
else {
pii le = len[1] , pp = ps[1] , tf = lf[1] , tr = rg[1];
if( mp( tf.fi - 1 , (int)( tf.se != 3 ) ) >= le ) pp = mp( 1 , tf.se == 3 ? 1 : ( tf.se ^ 3 ) ) , le = mp( tf.fi - 1 , (int)( tf.se != 3 ) );
if( mp( n - tr.fi , (int)( tr.se != 3 ) ) > le ) pp = mp( n , tr.se == 3 ? 1 : ( tr.se ^ 3 ) ) , le = mp( n - tr.fi , (int)( tr.se != 3 ) );
lsj[i] = pp , ins( 1 , 1 , n , pp );
printf("%d %d\n",pp.fi,pp.se);
}
} else {
int x; scanf("%d",&x);
era( 1 , 1 , n , lsj[x] );
}
}
}

signed main() {
// freopen("input","r",stdin);
// freopen("o.out","w",stdout);
// int T;cin >> T;while( T-- ) solve();
solve();
}

文章作者: yijan
文章链接: https://yijan.co/bzoj3340-ceoi2013-tram/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog