hdu 4302
有一个[0,l]区间,有一只毛毛虫,初始位置在0,现在又两种操作操作0 x 代表在x点放一块蛋糕,操作1代表毛毛虫要吃一块蛋糕:如果有蛋糕,那么毛毛虫将走到距离其最近的位置吃一块蛋糕。如果在其两边有两个距离其等距的蛋糕,那么毛毛虫将沿上次走的方向移动。如果没有蛋糕那么毛毛虫将待在原位置不动。现在给定M个操作,问毛毛虫的移动距离。
做法:
1.用线段树。线段树节点存储每个区间的放置有蛋糕的
最左点与最右点,对于0操作就是添加一个蛋糕,对于1操作就是先找到
移动的目标位置,然后减去一块蛋糕。
2.直接模拟,用set,快速查询,因为其有序。。代码短,速度快。
线段树版
/*
author : csuchenan
prog : hdu 4302
algorithm: 线段树 节点保存该区间的最左与最右值,然后每次查询查询双向
2012-10-20 16:41:23 Accepted 4302 421MS 5368K 4286 B C++ csu_chenan
*/
#include <cstdio>
#include <cstring>
const int maxn = 100010;
#define INF 10000000
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define M(x, y) ((x+y)>>1)
struct Tree{
int l, r;
int mx, mn, sum;
}tree[maxn*4];
inline int max(int x, int y){
if(x>y)
return x;
else
return y;
}
inline int min(int x, int y){
if(x<y)
return x;
else
return y;
}
void build(int l, int r, int c){
tree[c].l = l;
tree[c].r = r;
tree[c].mx = -INF;
tree[c].mn = INF;
tree[c].sum= 0;
if(l == r)
return ;
build(l, M(l, r), L(c));
build(M(l, r)+1, r, R(c));
}
void pushUp(int c){
int l = L(c);
int r = R(c);
tree[c].mx = max(tree[l].mx, tree[r].mx);
tree[c].mn = min(tree[l].mn, tree[r].mn);
return ;
}
//d=0 means delete d=1 means add
void update(int p, int d, int c){
if(tree[c].l == tree[c].r){
if(d==1){
tree[c].mx = p;
tree[c].mn = p;
tree[c].sum ++;
}
else{
tree[c].sum --;
if(tree[c].sum == 0){
tree[c].mx = -INF;
tree[c].mn = INF;
}
}
return ;
}
int m = M(tree[c].l, tree[c].r);
if(p <= m){
update(p, d, L(c));
}
else{
update(p, d, R(c));
}
pushUp(c);
}
//flag=1 means query left, so mx , flag=0 means query right, so mn;
int query(int l, int r, int c, int flag){
if(tree[c].l==l && tree[c].r==r){
if(flag==1)
return tree[c].mx;
else
return tree[c].mn;
}
int m = M(tree[c].l, tree[c].r);
if(r <= m){
return query(l, r, L(c), flag);
}
else if(l > m){
return query(l, r, R(c), flag);
}
else{
int tmp = query(l, m, L(c), flag);
int ans = query(m+1, r, R(c), flag);
if(flag==1){
return max(ans, tmp);
}
else{
return min(ans, tmp);
}
}
}
int main(){
int T, cas=1;
int l, m;
int c, x;
int dir, p, ans;
// freopen("test.in", "r", stdin);
scanf("%d", &T);
while(T--){
scanf("%d%d", &l, &m);
ans = p = 0;
dir = 1;
build(0, l, 1);
for(int i = 0; i < m; i ++){
scanf("%d", &c);
if(c==0){
scanf("%d", &x);
update(x, 1, 1);
}
else{
int lp = query(0, p, 1, 1);//left
int rp = INF;
if(p < l) // 注意这里。。。
rp = query(p+1, l, 1, 0);//right
//stay there
if(lp == p){
update(p, 0, 1);
}
//not found and stay there
else if(lp < 0 && rp > l){
}
//right
else if(lp < 0 && rp <= l){
dir = 1;
ans += rp - p;
p = rp;
update(p, 0, 1);
}
//left
else if(lp >= 0 && rp > l){
dir = 0;
ans += p - lp;
p = lp;
update(p, 0, 1);
}
//both
else if(lp >= 0 && rp <= l){
// right
if(p - lp > rp - p){
dir = 1;
ans += rp - p;
p = rp;
}
//left
else if(p - lp < rp - p){
dir = 0;
ans += p - lp;
p = lp;
}
else{
ans += p - lp;
if(dir==1){
p = rp;
}
else{
p = lp;
}
}
update(p, 0, 1);
}
}
}
printf("Case %d: %d\n", cas++, ans);
}
return 0;
}
set版
#include <cstdio>
#include <cstring>
#include <set>
using std::set;
const int maxn = 100005;
int num[maxn];
set<int> s;
int main(){
int T, cas = 1;
int c, a;
int l, m;
int dir, p, ans;
// freopen("test.in","r", stdin);
scanf("%d", &T);
while(T--){
scanf("%d%d", &l, &m);
memset(num,0, sizeof(int) * (l + 2));
ans = p = 0;
dir = 1;
s.clear();
s.insert(0);
for(int i = 1; i <= m; i ++){
scanf("%d", &c);
if(c==0){
scanf("%d", &a);
num[a] ++;
s.insert(a);
}
else{
int q = p;
if(num[p] > 0){
num[p] --;
continue;
}
set<int>::iterator it1, it2;
it1=it2=s.find(p);
int lp = -1, rp = -1;
if(it1 != s.begin()){
lp = *(--it1);
}
if(++it2 != s.end())
rp= *it2;
if(lp==-1 && rp== -1)
continue;
else{
if(lp==-1){
dir = 1;
ans += rp - p;
p = rp;
num[p] --;
}
else{
if(rp==-1){
dir = 0;
ans += p - lp;
p = lp;
num[p] --;
}
else{
int dl = p - lp;
int dr = rp - p;
if(dl==dr){
if(dir==1){
p = rp;
ans += dr;
num[p] --;
}
else{
p = lp;
ans += dl;
num[p] --;
}
}
else{
if(dl < dr){
dir = 0;
ans += dl;
p = lp;
num[p] --;
}
else{
dir = 1;
ans += dr;
p = rp;
num[p] --;
}
}
}
}
}
s.erase(q);
}
}
printf("Case %d: %d\n", cas ++ , ans);
}
}