【数据结构刷题记录】CF797G. Count the Trains(set,线段树)

考察set的灵活使用。本题要求输出原序列严格递减子序列的长度,set存每一段相同的子串的头下标。在询问时,首先明白每次操作最多能使答案+1,产生的原因是a[k]-d使得原本大于之前的a[k]现在小于前面那一段了,因此k可以插入到set里面。同时a[k] - d也使得当前的a[k]可能会把之后原本小于a[k]的现在大于a[k] - d了,a[k]会抹平这些元素,因此遍历删除即可。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <math.h>
#include <map>
#include <set>
#include <queue>

using namespace std;
#define endl '\n'
const int maxn = 1e5 + 5;
int a[maxn],n,q;
void solve(){
    cin >> n >> q;
    set<int> s;
    int last = 1;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        if (s.empty() || a[i] < a[last]){
            last = i;
            s.insert(i);
        }
    }
    while (q --){
        int k,d;
        cin >> k >> d;
        a[k] -= d;
        auto p = s.upper_bound(k);
        if (p != s.begin()){
            p = prev(p);
            if (a[*p] > a[k]) s.insert(k);
        }
        p = s.upper_bound(k);
        while (true){
            if (p == s.end()) break;
            if (a[k] <= a[*p]){
                p = s.erase(p);
            } else break;
        }
        cout << s.size() << ' ';
    }
    cout << endl;
}
signed main(){
    std::ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
#ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    int T = 1;
    cin >> T;
    while (T--) solve();

}

下面再提供一下此题的线段树做法
线段树进行区间合并即可,需要考虑许多细节,请参考《楼房重建》

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <math.h>
#include <map>
#include <set>
#include <queue>

using namespace std;
#define endl '\n'
#define lson node << 1
#define rson node << 1 | 1
const int maxn = 1e5 + 5;
struct node{
    int val;
    double maxx;
}t[maxn << 2];
double a[maxn];
int n,q;
void pushup1(int node){
    t[node].maxx = min(t[lson].maxx,t[rson].maxx);
}
int pushup2(int node,double lx,int l,int r){
    int mid = l + r >> 1;
    if (l == r) return a[l] < lx;
    if (t[node].maxx >= lx) return 0;
    if (a[l] < lx) return t[node].val;
    if (t[lson].maxx >= lx) return pushup2(rson,lx,mid + 1,r);
    return pushup2(lson,lx,l,mid) + t[node].val - t[lson].val;
}
void build(int l,int r,int node){
    if (l == r){
        t[node].maxx = a[l];
        t[node].val = 1;
        return;
    }
    int mid = l + r >> 1;
    build(l,mid,lson);
    build(mid + 1,r,rson);
    pushup1(node);
    t[node].val = t[lson].val + pushup2(rson,t[lson].maxx,mid + 1,r);
}
void update(int l,int r,int node,int x,double k){
    if (l == r){
        t[node].maxx = k;
        t[node].val = 1;
        return;
    }
    int mid = l + r >> 1;
    if (x <= mid) update(l,mid,lson,x,k);
    else update(mid + 1,r,rson,x,k);
    pushup1(node);
    t[node].val = t[lson].val + pushup2(rson,t[lson].maxx,mid + 1,r);
}
void solve(){
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    build(1,n,1);
    while (q --){
        int x,y;
        cin >> x >> y;
        a[x] -= y;
        update(1,n,1,x,a[x]);
        cout << t[1].val << ' ';
    }
    cout << endl;


}
signed main(){
    std::ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
#ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    int T = 1;
    cin >> T;
    while (T--) solve();

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值