您的位置:首页 > 其它

51Nod-1461-稳定桌

2017-06-29 05:53 363 查看
ACM模版

描述



题解

这个是 CF 上的原题改的,将 di 的数据放大了,所以不能普通的枚举代价了,而需要用数据结构优化处理,这里可以使用线段树搞搞事情,很意外。

首先,我们对每种代价进行建树,然后从大到小对长度进行排序,接着遍历一遍,枚举桌腿高度,不断添加删除的边,因为所有高于当前桌腿高度的都要删除(真正的从线段树中删除),然后判断低于当前桌腿高度的这些桌腿的数目是否少于当前高度的数量,如果是的话,直接更新结果,如果不是,那么我们需要从低于当前桌腿高度的桌腿中查找若干个代价最低的桌腿删除(并不是真的删除,只是查找线段树中的这部分结果而已),这个过程十分适合用线段树处理,所以呢,这个题就酱紫过了。

总而言之,这个题是一道可以用线段树优化的枚举贪心问题。

写这个代码时我遇见了一个极其隐秘的 bug,但是这个 bug 一点也不稀罕,这个 bug 是由于宏定义的 MIN 导致的。

我定义了一个 MIN(a,b) ((a)>(b)) ? (b):(a) 的宏定义,然后在代码中调用了,代码如下:

ans = MIN(ans, sum + query(1, MAXN, 1));


宏替换后就变成了这个,

ans = ans > (sum + query(1, MAXN, 1)) ? (sum + query(1, MAXN, 1)) : ans;


如此一来,我的 query() 会多调用若干次,而代码中很清楚的是,这并不是单纯的访问,ld 会在访问时产生变化,影响下次的访问。

这个问题,让我找了一个多小时的 bug,虽然我宏定义这个玩意儿时格外注意了缺少括号所可能带来的 bug,但是我终究还是没有注意到函数重复调用上的 bug……

╮(╯▽╰)╭哎,像我这么粗心大意的人,还是少用宏定义吧,动不动就引入了这种极度隐秘的 bug,心塞( ⊙ o ⊙ )啊!

代码

#include <cstdio>
#include <iostream>
#include <algorithm>

#define lson k << 1
#define rson k << 1 | 1

using namespace std;

typedef long long ll;

const int MAXN = 1e5 + 10;
const ll INF = 0x3f3f3f3f3f3f3f3f;

template <class T>
inline void scan_d(T &ret)
{
char c;
ret = 0;
while ((c = getchar()) < '0' || c > '9');
while (c >= '0' && c <= '9')
{
ret = ret * 10 + (c - '0'), c = getchar();
}
}

struct leg
{
int l, d;
bool operator < (const leg &b) const
{
return l > b.l;
}
} L[MAXN];

struct node
{
int c;
ll v;
} tree[MAXN << 2];

int n;
int cnt[MAXN];      //  每种代价的数量

inline void pushup(int k)
{
tree[k].v = tree[lson].v + tree[rson].v;
tree[k].c = tree[lson].c + tree[rson].c;
}

void build(int l, int r, int k)
{
if (l == r)
{
if (cnt[l])
{
tree[k].v = 1ll * (tree[k].c = cnt[l]) * l;
}
return ;
}
int m = (l + r) >> 1;
build(l, m, lson);
build(m + 1, r, rson);
pushup(k);
}

int ld, flag;
void update(int l, int r, int k)
{
if (flag == -1)
{
tree[k].c--, tree[k].v -= ld;
}
else
{
tree[k].c++, tree[k].v += ld;
}

if (l == r)
{
return ;
}
int m = (l + r) >> 1;
if (ld <= m)
{
update(l, m, lson);
}
else
{
update(m + 1, r, rson);
}
}

ll query(int l, int r, int k)
{
if (l == r)
{
return 1ll * ld * l;
}

int m = (l + r) >> 1;
if (tree[lson].c >= ld)
{
return query(l, m, lson);
}
else
{
ld -= tree[lson].c;
return tree[lson].v + query(m + 1, r, rson);
}
}

int main()
{
scan_d(n);
ll ans = INF, sum = 0;
for (int i = 1; i <= n; i++)
{
scan_d(L[i].l);
}
for (int i = 1; i <= n; i++)
{
scan_d(L[i].d);
cnt[L[i].d]++;
}

build(1, MAXN, 1);
sort(L + 1, L + n + 1);

ll tmp = 0;
for (int i = 1, j; i <= n; i = j + 1)
{
if (sum >= ans)
{
break;
}

sum += tmp;
j = i;
tmp = ld = L[i].d;
flag = -1;
update(1, MAXN, 1);

while (j < n && L[j + 1].l == L[i].l)
{
tmp += (ld = L[++j].d);
flag = -1;
update(1, MAXN, 1);
}

int num = j - i;    //  当前高度桌腿的数目
if (n - j <= num)
{
ans = min(ans, sum);
break;
}
else
{
ld = n - j - num;
ans = min(ans, sum + query(1, MAXN, 1));
}
}

cout << ans << endl;

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树 枚举 贪心