您的位置:首页 > 其它

Codeforces Round #218 (Div. 2) (线段树区间处理)

2013-12-08 21:26 453 查看
A,B大水题,不过B题逗比了题意没理解清楚,讲的太不清楚了感觉= =还是英语弱,白白错了两发。

C:

二分答案判断是否可行,也逗比了下。。。二分的上界开太大导致爆long long了。。。

#include
#include
#include
using namespace std;
typedef __int64 ll;

char s[111];
int B, S, C, pb, ps, pc;
ll r, cnt[222];

int ok(ll m) {
ll tot = r;
ll BB = cnt['B'-'A']*m, SS = cnt['S'-'A']*m, CC = cnt['C'-'A']*m;
if(BB > B) {
BB -= B;
tot -= BB*pb;
}
if(SS > S) {
SS -= S;
tot -= SS*ps;
}
if(CC > C) {
CC -= C;
tot -= CC*pc;
}
return tot >= 0;
}

int main() {
scanf("%s", s);
int len = strlen(s);
for(int i = 0;i < len; i++) cnt[s[i]-'A']++;
scanf("%d%d%d", &B, &S, &C);
scanf("%d%d%d", &pb, &ps, &pc);
scanf("%I64d", &r);
ll L = 0, R = 100000000000000LL;
ll ans = 0;
while(L <= R) {
ll mid = (L + R)/2;
if(ok(mid)) {
ans = mid;
L = mid+1;
}
else
R = mid-1;
}
printf("%I64d\n", ans);
return 0;
}


D:

看完题想都不想就用线段树搞起了,虽然我知道并查集很简单。。不过没用并查集写过区间处理的问题就不用多想了。。

刚开始时,所有的船都是空的,也就是说可放的水量是船的容量,然后对于第一个操作,意思就是从第x个数开始减小直到减小了p或者后面都变成0了。这里我就二分查找下最右边的会减小的数R,然后把x~R-1的数都清零,对于第R个数计算出剩余量后单点更新,mark[rt] = true表示这段区间被清零了,第二个操作直接单点询问。

#include
#include
#include
using namespace std;
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
typedef __int64 ll;

const int maxn = 200000 + 10;

ll sum[maxn<<2], a[maxn];
bool mark[maxn<<2];
int n;

void up(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void create(int rt, int l, int r) {
mark[rt] = false;
if(l == r) {
sum[rt] = a[l];
return ;
}
int mid = (l + r)/2;
create(lson);
create(rson);
up(rt);
}

void down(int rt) {
if(mark[rt]) {
mark[rt<<1] = mark[rt<<1|1] = true;
sum[rt<<1] = sum[rt<<1|1] = 0;
}
}

void update1(int rt, int l, int r, int L, int R) {
if(L > R)   return;
if(L <= l && R >= r) {
mark[rt] = true;
sum[rt] = 0;
return ;
}
down(rt);
int mid = (l + r)/2;
if(L <= mid)    update1(lson, L, R);
if(R > mid) update1(rson, L, R);
up(rt);
}

void update2(int rt, int l, int r, int x, ll val) {
if(l == r) {
sum[rt] = val;
return ;
}
down(rt);
int mid = (l + r)/2;
if(x <= mid)    update2(lson, x, val);
else    update2(rson, x, val);
up(rt);
}

ll query(int rt, int l, int r, int L, int R) {
if(L <= l && R >= r) {
return sum[rt];
}
down(rt);
int mid = (l + r)/2;
ll ret = 0;
if(L <= mid)    ret += query(lson, L, R);
if(R > mid) ret += query(rson, L, R);
up(rt);
return ret;
}

int main() {
scanf("%d", &n);
for(int i = 1;i <= n; i++)  scanf("%I64d", &a[i]);
create(1, 1, n);
int m, op, x, p;
scanf("%d", &m);
while(m--) {
scanf("%d", &op);
if(op == 1) {
scanf("%d%d", &x, &p);
int L = x, R = n, pos = n;
ll tot = -1;
while(L <= R) {
int mid = (L + R)/2;
ll cur = query(1, 1, n, x, mid);
if(cur >= p) {
tot = cur; pos = mid;
R = mid-1;
}
else
L = mid+1;
}
//            printf("x = %d p = %d tot = %I64d pos = %d\n", x, p, tot, pos);
if(tot == -1) {
update1(1, 1, n, x, n);
}
else {
update1(1, 1, n, x, pos-1);
//        printf("!!!!!! %d\n", tot - p);
update2(1, 1, n, pos, tot - p);
}
}
else {
scanf("%d", &x);
printf("%I64d\n", a[x] - query(1, 1, n, x, x));
}
//      printf("%I64d %I64d\n", query(1, 1, n, 1, 1), query(1, 1, n, 2, 2));
}
return 0;
}


E:

给你n个地铁站的位置,都是在x轴上的点,选出k个点使得k个点两两之间的距离和最小。

思路:

首先肯定是对n个点排序,这样子之后k个点肯定是排完序之后某段连续的k个。

所以可以枚举起始位置计算该位置开始的k个距离和,现在的问题就是如何O(1)的计算出该位置的结果。

假设我知道了 区间(L, R)的两两距离和,考虑区间 (L , R) 如何O(1)推出区间(L+1, R+1),转移其实就是去掉L点,加上R+1点,很容易知道原来的距离和减小了L到其他点的距离s1,增加了R+1到其他点的距离s2,画个图很容易得到s1+s2 = (L到R+1的距离)*(k-1),也就是说我只需要知道了L到其他点的距离和,就可以推出转移之后的两两距离和的值,所以说只需要维护上一次的两两距离和and 前一个区间的L到其他点的距离和即可。具体见代码~

这个E题实在是伤,比赛最后十分钟敲完交上了WA,最后竟然是给定的每个位置不是递增的,输出的时候要输出id,前面居然没把这个写上来= =伤,还是太弱

#include
#include
#include
using namespace std;
typedef __int64 ll;

const int maxn = 300000 + 10;

ll a[maxn], sum[maxn];

struct PP {
int x, id;
PP() {}
PP(int x, int id): x(x), id(id) {}
bool operator < (const PP &a) const {
return x < a.x;
}
}st[maxn];

int main() {
int n, k;
scanf("%d", &n);
for(int i = 1;i <= n; i++)  scanf("%d", &st[i].x), st[i].id = i;
sort(st + 1, st + n + 1);
a[1] = 0;
for(int i = 2;i <= n; i++)  a[i] = a[i-1] + st[i].x - st[i-1].x;
for(int i = 1;i <= n; i++)  sum[i] = sum[i-1] + a[i];
scanf("%d", &k);
ll cur = 0, des = 0;
for(int i = 2;i <= k; i++) {
des += a[i]*(k-i);
cur += sum[k] - des;
}
ll mx = cur;
int ans = 1;
ll pre = sum[k];
// cur当前区间两两距离和,pre为当前区间L到其他点的距离和
for(int i = 2;i+k-1 <= n; i++) {
cur = (a[i+k-1] - a[i-1])*(k-1) - pre*2 + cur;
if(cur < mx) {
ans = i;
mx = cur;
}
pre = pre - (a[i] - a[i-1])*(k-1) + a[i+k-1] - a[i];
}
while(k--) {
printf("%d ", st[ans++].id);
}
puts("");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐