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

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;
}

     
     
    
    
   
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值