CodeForces 1136 E 和 D 題 有意思的題

題目鏈接:here
原題目描述在最下面
先更新E題

CF 1136E 線段樹

題意:
給你兩個序列a1,a2...ana_1,a_2...a_nk1,k2...kn1k_1,k_2...k_{n-1}。輸入保證ai+kiai+1a_i+k_i\le a_{i+1}
兩種操作:區間求i=lrai\sum_{i=l}^ra_i和給apa_p單點加xx
第二個操作有連鎖反映,若ap+kp>ap+1a_p+k_p\gt a_{p+1},則ap+1a_{p+1}更新爲ap+kpa_p+k_p,同理更新ap+2a_{p+2}直到不能更新爲止。

思路:
在紙上畫畫,你會發現:a1a2k1a3k2k1...ani=1n1kia_1\le a_2-k_1\le a_3-k_2-k_1...\le a_n-\sum_{i=1}^{n-1}k_i
pp位置加上值xx之後,api=1p1kia_p-\sum_{i=1}^{p-1}k_i也增加了xx。更新之前已知:api=1p1kiap+1i=1pkia_p-\sum_{i=1}^{p-1}k_i\le a_{p+1}-\sum_{i=1}^pk_i,如果ap+kp+x>ap+1a_p+k_p+x\gt a_{p+1},那麼ap+1=ap+kp+xa_{p+1}=a_p+k_p+x,並且ap+1i=1pki=api=1p1ki+xa_{p+1}-\sum_{i=1}^pk_i=a_p-\sum_{i=1}^{p-1}k_i+xap+2a_{p+2}同理更新。

你發現了什麼?

我們令bx=axi=1x1kib_x=a_x-\sum_{i=1}^{x-1}k_i,我們給pp單點加上xx之後的效果是:把i[p+1,n]i\in[p+1,n]間所有小於bp+xb_p+xbib_i全部賦值bp+xb_p+x

到這裏思路就很明顯了。我們用線段樹維護bxb_x即可,求和就是i=lrbi+i=lrj=1i1kj\sum_{i=l}^rb_i+\sum_{i=l}^r\sum_{j=1}^{i-1}k_j;更新就是區間賦值操作,因爲bxb_x滿足單調不遞減,所以你可以二分出右端點來。

本題結束。

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(1e5)n(1e5)個人,m(1e5)m(1e5)對關係pair(x,y)pair(x,y)表示如果編號爲xx的人恰好排在編號爲yy的人前面,那麼(x,y)(x,y)可以互換位置。
給你初始這nn個人的排隊順序,問你NastyaNastya最多能往前移動多少格位置,這人初始在最後面。
思路:
記得當時看到這題想了各種圖論的方法,或者xjb搜索,感覺好像都不太行得通?
這個題走的順序很有特點。

記錄答案ansans爲當前排在最後一個人後面的人的個數(就是換位置換到後面去的)。
rs[i]rs[i]記錄爲第ii個人後面有多少個人可以和你交換位置。
顯然if(rs[i]==nians)if(rs[i] == n - i - ans)表示第ii個人和NastyaNastya間的人都可以和第i個人換位置,那麼他就可以直接排到最後面去;
否則更新可以和他交換位置的人的rs[]rs[]

正確性是顯然的,因爲所有排到NastyaNastya後面的人都不會對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;
}

原題目描述

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章