CodeForces 1136 E 和 D 题 有意思的题

题目链接:here
原题目描述在最下面
先更新E题

CF 1136E 线段树

题意:
给你两个序列 a 1 , a 2 . . . a n a_1,a_2...a_n a1,a2...an k 1 , k 2 . . . k n − 1 k_1,k_2...k_{n-1} k1,k2...kn1。输入保证 a i + k i ≤ a i + 1 a_i+k_i\le a_{i+1} ai+kiai+1
两种操作:区间求 ∑ i = l r a i \sum_{i=l}^ra_i i=lrai和给 a p a_p ap单点加 x x x
第二个操作有连锁反映,若 a p + k p > a p + 1 a_p+k_p\gt a_{p+1} ap+kp>ap+1,则 a p + 1 a_{p+1} ap+1更新为 a p + k p a_p+k_p ap+kp,同理更新 a p + 2 a_{p+2} ap+2直到不能更新为止。

思路:
在纸上画画,你会发现: a 1 ≤ a 2 − k 1 ≤ a 3 − k 2 − k 1 . . . ≤ a n − ∑ i = 1 n − 1 k i a_1\le a_2-k_1\le a_3-k_2-k_1...\le a_n-\sum_{i=1}^{n-1}k_i a1a2k1a3k2k1...ani=1n1ki
p p p位置加上值 x x x之后, a p − ∑ i = 1 p − 1 k i a_p-\sum_{i=1}^{p-1}k_i api=1p1ki也增加了 x x x。更新之前已知: a p − ∑ i = 1 p − 1 k i ≤ a p + 1 − ∑ i = 1 p k i a_p-\sum_{i=1}^{p-1}k_i\le a_{p+1}-\sum_{i=1}^pk_i api=1p1kiap+1i=1pki,如果 a p + k p + x > a p + 1 a_p+k_p+x\gt a_{p+1} ap+kp+x>ap+1,那么 a p + 1 = a p + k p + x a_{p+1}=a_p+k_p+x ap+1=ap+kp+x,并且 a p + 1 − ∑ i = 1 p k i = a p − ∑ i = 1 p − 1 k i + x a_{p+1}-\sum_{i=1}^pk_i=a_p-\sum_{i=1}^{p-1}k_i+x ap+1i=1pki=api=1p1ki+x a p + 2 a_{p+2} ap+2同理更新。

你发现了什么?

我们令 b x = a x − ∑ i = 1 x − 1 k i b_x=a_x-\sum_{i=1}^{x-1}k_i bx=axi=1x1ki,我们给 p p p单点加上 x x x之后的效果是:把 i ∈ [ p + 1 , n ] i\in[p+1,n] i[p+1,n]间所有小于 b p + x b_p+x bp+x b i b_i bi全部赋值 b p + x b_p+x bp+x

到这里思路就很明显了。我们用线段树维护 b x b_x bx即可,求和就是 ∑ i = l r b i + ∑ i = l r ∑ j = 1 i − 1 k j \sum_{i=l}^rb_i+\sum_{i=l}^r\sum_{j=1}^{i-1}k_j i=lrbi+i=lrj=1i1kj;更新就是区间赋值操作,因为 b x b_x bx满足单调不递减,所以你可以二分出右端点来。

本题结束。

AC_code

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;

const LL lt = -1e18;
const int INF = 0x3f3f3f3f;
const int MXN = 1e5 + 7;

int n, m;
LL ar[MXN], kr[MXN], kk[MXN];
LL sum[MXN<<2], flag[MXN<<2], Max[MXN<<2];
void build(int l, int r, int rt) {
    flag[rt] = lt;
    if(l == r) {
        sum[rt] = ar[l] - kr[l-1];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, rt<<1); build(mid+1, r, rt<<1|1);
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void push_down(int l, int mid, int r, int rt) {
    if(flag[rt] == lt) return;
    flag[rt<<1] = flag[rt];
    flag[rt<<1|1] = flag[rt];
    sum[rt<<1] = flag[rt] * (mid-l+1);
    sum[rt<<1|1] = flag[rt] * (r-mid);
    flag[rt] = lt;
}
void update(int L, int R, LL v, int l, int r, int rt) {
    if(L <= l && r <= R) {
        flag[rt] = v;
        sum[rt] = v * (r-l+1);
        return;
    }
    int mid = (l + r) >> 1;
    push_down(l, mid, r, rt);
    if(L > mid) update(L,R,v,mid+1,r,rt<<1|1);
    else if(R <= mid) update(L,R,v,l,mid,rt<<1);
    else {
        update(L,mid,v,l,mid,rt<<1);
        update(mid+1,R,v,mid+1,r,rt<<1|1);
    }
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
LL query(int L, int R, int l, int r, int rt) {
    if(L > R) return 0;
    if(L <= l && r <= R) return sum[rt];
    int mid = (l+r)>>1;
    push_down(l, mid, r, rt);
    if(L > mid) return query(L,R,mid+1,r,rt<<1|1);
    else if(R <= mid) return query(L,R,l,mid,rt<<1);
    else {
        return query(L,mid,l,mid,rt<<1)+query(mid+1,R,mid+1,r,rt<<1|1);
    }
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lld", &ar[i]);
    for(int i = 1; i < n; ++i) scanf("%lld", &kr[i]);
    for(int i = 2; i < n; ++i) kr[i] += kr[i-1];
    for(int i = 1; i < n; ++i) kk[i] = kk[i-1] + kr[i];
    build(1, n, 1);
    int Q; scanf("%d", &Q);
    char s[2]; int l, r;
    while(Q --) {
        scanf("%s%d%d", s, &l, &r);
        if(s[0] == 's') {
            printf("%lld\n", query(l,r,1,n,1)+kk[r-1]-(l>=2?kk[l-2]:0));
        }else {
            int L = l, R = n, mid, ans = l;
            ar[l] = query(l,l,1,n,1) + r;
            //printf("*%lld\n", ar[l]);
            while(L <= R) {
                mid = (L+R) >> 1;
                if(ar[l] > query(mid,mid,1,n,1)) {
                    ans = mid;
                    L = mid+1;
                }else R = mid-1;
            }
            //printf("%d\n", ans);
            update(l, ans, ar[l], 1, n, 1);
        }
    }
    return 0;
}

CF 1136D 贪心

题意:
一个排队游戏, n ( 1 e 5 ) n(1e5) n(1e5)个人, m ( 1 e 5 ) m(1e5) m(1e5)对关系 p a i r ( x , y ) pair(x,y) pair(x,y)表示如果编号为 x x x的人恰好排在编号为 y y y的人前面,那么 ( x , y ) (x,y) (x,y)可以互换位置。
给你初始这 n n n个人的排队顺序,问你 N a s t y a Nastya Nastya最多能往前移动多少格位置,这人初始在最后面。
思路:
记得当时看到这题想了各种图论的方法,或者xjb搜索,感觉好像都不太行得通?
这个题走的顺序很有特点。

记录答案 a n s ans ans为当前排在最后一个人后面的人的个数(就是换位置换到后面去的)。
r s [ i ] rs[i] rs[i]记录为第 i i i个人后面有多少个人可以和你交换位置。
显然 i f ( r s [ i ] = = n − i − a n s ) if(rs[i] == n - i - ans) if(rs[i]==nians)表示第 i i i个人和 N a s t y a Nastya Nastya间的人都可以和第i个人换位置,那么他就可以直接排到最后面去;
否则更新可以和他交换位置的人的 r s [ ] rs[] rs[]

正确性是显然的,因为所有排到 N a s t y a Nastya Nastya后面的人都不会对 r s [ ] rs[] rs[]造成贡献,这个判断是无误的。

AC_code

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;

const int INF = 0x3f3f3f3f;
const int MXN = 5e5 + 7;

int n, m;
int is[MXN];
int ls[MXN], rs[MXN];
std::vector<int> mp[MXN];
int main() {
#ifndef ONLINE_JUDGE
    freopen("E://ADpan//in.in", "r", stdin);
    //freopen("E://ADpan//out.out", "w", stdout);
#endif
    scanf("%d%d", &n, &m);
    for(int i = 1, x; i <= n; ++i) scanf("%d", &x), is[x] = i;
    for(int i = 0, a, b; i < m; ++i) {
        scanf("%d%d", &a, &b); a = is[a], b = is[b];
        mp[b].push_back(a);
        if(b == n) ++ rs[a];
    }
    int ans = 0;
    for(int i = n - 1; i >= 1; --i) {
        //printf("%d %d\n", i, rs[i]);
        if(rs[i] == n - i - ans) {
            ++ ans;
        }else {
            for(int x: mp[i]) ++ rs[x];
        }
    }
    printf("%d\n", ans);
#ifndef ONLINE_JUDGE
    cout << "time cost:" << clock() << "ms" << "\n";
#endif
    return 0;
}
原题目描述

在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值