4月4号 机房训练赛(数据结构)

我只是循规蹈矩地生活,惯于离经叛道,体味心安理得。亦于按部就班,痛感乏善可陈。我欺骗过,伪装过,失真过,但置我于死地者,必将赐我以后生。

这次考试太惨,直接说题目吧。
这里写图片描述
这道题十分之坑爹,如果只有赋值操作或只有增加操作那十分简单,直接敲区间修改线段树模板就好了。但它要求支持两个操作。
那么我们要这样想。
因为它有两个操作,增值和赋值。那么我们设置两个lazy标记分别表示增值的和赋值的量(代码中我们用value增值,delta赋值)。然后咋整呢?
我们讨论一下,1、原来的线段上有增值标记那么我们进行增值操作的时候之间标记加值就好了。如果是赋值操作那么就覆盖掉原来的增值。等等。。。。
所有讨论如下:
空标记+ 赋值操作= 赋值标记
空标记+ 增量操作= 增量标记
增量标记+ 赋值操作= 赋值标记
增量标记+ 增量操作= 增量标记
赋值标记+ 增量操作= 赋值标记
赋值标记+ 赋值操作= 赋值标记
所以我们再设置一个type表示无标记、增值标记和赋值标记。
代码中我们用0无,1增值,2赋值来表示。
然后就是码代码了,详解见代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100005
#define dnt long long
using namespace std;
int aa[N], n, m, x, y, z;
struct Node;
void change( Node *nd, int lf, int rg, int L, int R, int val );
void modify( Node *nd, int lf, int rg, int L, int R, int val );
struct Node{
    dnt sum;
    int type;
    dnt value, delta;// 0 无 1 增值 2 赋值 value 增值 delta 赋值 
    Node *ls, *rs;
    void pushdown(int lf, int rg) {// pushdown的时候我们讨论type的种类 
        if ( type == 0 ) return;// 为0 无标记 返回 
        if ( type == 1 ) { // 1 为增值用change修改 
            int mid = (lf + rg) >> 1;
            change(ls, lf, mid, lf, mid, value);
            change(rs, mid+1, rg, mid+1, rg, value);
            type = 0;
        }
        else {// 2 为赋值用modify修改 
            int mid = (lf + rg) >> 1;
            modify(ls, lf, mid, lf, mid, delta);
            modify(rs, mid+1, rg, mid+1, rg, delta);
            type = 0;
        }
    }
    void update() {
        sum = ls->sum + rs->sum;
    }
};
Node pool[N * 2], *tail = pool, *root;
Node *build(int lf, int rg) {//普通的建树 
    Node *nd = ++tail;
    if ( lf == rg ) {
        nd->sum = aa[lf];
        nd->type = 0;
    }
    else {
        int mid = (lf + rg) >> 1;
        nd->ls = build(lf, mid);
        nd->rs = build(mid+1, rg);
        nd->update();
        nd->type = 0;
    }
    return nd;
}
void change( Node *nd, int lf, int rg, int L, int R, int val ) {
    if ( L <= lf && rg <= R ) {
        nd->sum += (dnt)(rg - lf + 1) * (dnt)val;
        if ( nd->type == 0 ) {
            nd->type = 1;
            nd->value = val; // 注意这里要用赋值,不用 += 因为这里type为0表示无标记所以要重新给值(具体原因是pushdown的时候未清两个lazy标记的值) 
        }
        else if ( nd->type == 1) nd->value += val; // 增值和增值操作标记种类不变,在增值标记上加 
            else nd->delta += val; // 赋值对增值操作为赋值标记,所以在赋值标记上加 
        return;
    }
    else {
        nd->pushdown(lf, rg);
        int mid = (lf + rg) >> 1;
        if ( L <= mid )
            change(nd->ls, lf, mid, L, R, val);
        if ( R > mid)
            change(nd->rs, mid+1, rg, L, R, val);
        nd->update();
    }
}
void modify( Node *nd, int lf, int rg, int L, int R, int val ) {
    if ( L <= lf && rg <= R ) {
        nd->sum = (dnt)(rg - lf + 1) * (dnt)val;
        nd->type = 2;// 赋值操作直接变成2 十分的霸道~~~ 
        nd->delta = val; //赋值标记改变 
        return;
    }
    else {
        nd->pushdown(lf, rg);
        int mid = (lf + rg) >> 1;
        if ( L <= mid )
            modify(nd->ls, lf, mid, L, R, val);
        if ( R > mid)
            modify(nd->rs, mid+1, rg, L, R, val);
        nd->update();
    }
}
dnt query( Node *nd, int lf, int rg, int L, int R ) {//普通的查询 
    dnt rt = 0;
    if ( L <= lf && rg <= R ) {
        return nd->sum;
    }
    int mid = (lf + rg) >> 1;
    nd->pushdown(lf , rg);
    if ( L <= mid ) rt += query(nd->ls, lf, mid, L, R);
    if ( R > mid ) rt += query(nd->rs, mid+1, rg, L, R);
//  nd->update();
    return rt;  
}
int main() {
    freopen("setmod.in","r",stdin);
    freopen("setmod.out","w",stdout);
    scanf( "%d%d", &n, &m );
    for ( int i = 1; i <= n; i++ )
    scanf( "%d", &aa[i] );
    root = build(1, n);
    for ( int i = 1; i <= m; i++ ) {// x > y 左端大于右端非法情况。 
        char s[10];
        scanf( "%s", s );
        if ( s[0] == 'c' ) {
            scanf( "%d%d%d", &x, &y, &z );
            if ( x > y ) continue;
            change(root, 1, n, x, y, z);
        }
        if ( s[0] == 'q' ) {
            scanf( "%d%d", &x, &y );
            if ( x > y ) {
                printf("0\n");
                continue;
            }
            printf( "%I64d\n", query(root, 1, n, x, y) );
        }
        if ( s[0] == 'm' ) {
            scanf( "%d%d%d", &x, &y, &z );
            if ( x > y ) continue;
            modify(root, 1, n, x, y, z);
        }
    }
    return 0;
}

这里写图片描述
模板题:动态主席树(用树状数组套值域线段树实现)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define dnt long long
#define clear(a) memset(a, 0, sizeof(a))
#define N 100005
#define lowbit(x) x & -x
using namespace std;
int n, m, aa[N], ca, cb, x, y, k;
char s[10];
struct Node{
    int siz;
    Node *ls, *rs;
};
struct Node pool[200 * N], *tail = pool, *roots[N], *null;
struct Node *va[N], *vb[N];
Node *newnode() {
    Node *nd = ++tail;
    nd->ls = null;
    nd->rs = null;
    nd->siz = 0;
    return nd;
}
void init() {
    null = ++tail;
    null->ls = null;
    null->rs = null;
    null->siz = 0;
}
void modify( int lf, int rg, int pos, int delta ) {
    for ( int i = 1; i <= ca; i++ )
    va[i]->siz += delta;
    if ( lf == rg ) return;
    int mid = (lf + rg) >> 1;
    if ( pos <= mid ) {
        for ( int i = 1; i <= ca; i++ ) {
            if ( va[i]->ls == null )
            va[i]->ls = newnode();
            va[i] = va[i]->ls;
        }
        modify(lf, mid, pos, delta);
    }
    else {
        for ( int i = 1; i <= ca; i++ ) {
            if ( va[i]->rs == null )
            va[i]->rs = newnode();
            va[i] = va[i]->rs;
        }
        modify(mid+1, rg, pos, delta);
    }
}
void modify( int u, int pos, int delta ) {
    ca = 0;
    for ( int i = u; i <= n; i += lowbit(i) )
    va[++ca] = roots[i];
    modify(1, n, pos, delta);
}
void modify( int u, int val ) {
    modify( u, aa[u], -1 );
    modify( u, val, 1 );
    aa[u] = val;
}
int query_seg( int lf, int rg, int k ) {
    if ( lf == rg ) return lf;
    int lz = 0;
    for ( int i = 1; i <= ca; i++ ) lz -= va[i]->ls->siz;
    for ( int i = 1; i <= cb; i++ ) lz += vb[i]->ls->siz;
    int mid = (lf + rg) >> 1;
    if ( k <= lz ) {
        for ( int i = 1; i <= ca; i++ )
        va[i] = va[i]->ls;
        for ( int i = 1; i <= cb; i++ )
        vb[i] = vb[i]->ls;
        return query_seg(lf, mid, k);
    }
    else {
        for ( int i = 1; i <= ca; i++ )
        va[i] = va[i]->rs;
        for ( int i = 1; i <= cb; i++ )
        vb[i] = vb[i]->rs;
        k -= lz;
        return query_seg(mid+1, rg, k); 
    }
}
int query( int l, int r, int k ) {
    ca = cb = 0;
    for ( int i = l-1; i; i -= lowbit(i) ) va[++ca] = roots[i];
    for ( int i = r; i; i -= lowbit(i) ) vb[++cb] = roots[i];
    return query_seg(1, n, k);
}
int main() {
    scanf( "%d%d", &n, &m );
    for ( int i = 1; i <= n; i++ )
    scanf( "%d", &aa[i] );
    init();
    for ( int i = 0; i <= n; i++ )
    roots[i] = newnode();
    for ( int i = 1; i <= n; i++ )
    modify(i, aa[i], 1);
    while ( m-- ) {
        scanf( "%s", s );
        if ( s[0] == 'm' ) {
            scanf( "%d%d", &x, &y );
            modify( x, y );
        }
        else {
            scanf( "%d%d%d", &x, &y, &k );
            printf( "%d\n", query(x, y, k) );
        }
    }
    return 0;
}

思念着你

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值