CDOJ 1292 卿学姐种花(暴力,分块,线段树)

解决一个关于在连续花坛中种植花朵的问题,采用不同的数据结构和技术实现高效的操作更新与查询。

众所周知,在喵哈哈村,有一个温柔善良的卿学姐。

卿学姐喜欢和她一样美丽的花。所以卿学姐家的后院有很多的花坛。

卿学姐有n

个花坛,一开始第i个花坛里有A[i]

朵花。每过一段时间,卿学姐都会在花坛里种上新的花。

作为一个聪明的学姐,卿学姐的种花方式也是与众不同 , 每一次,卿学姐会在第x

个花坛种上y朵花,然后在第x+1个花坛上种上y1朵花,再在第x+2个花坛上种上y2

朵花......以此类推,直到种到最后一个花坛,或者不需要种花为止。

喵哈哈的村民们都喜欢去卿学姐的后院赏花,沈宝宝也不例外。然而沈宝宝可不是省油的灯,怎么可能会老老实实地赏花呢。每次沈宝宝来时,都会随机询问卿学姐在第i

个花坛有多少朵花。

花坛的花实在太多了,卿学姐实在是数不过来。于是现在她向你求助,希望你能帮她数出花坛里多少朵花。

Input

第一行输入两个整数,花坛个数N

和操作次数Q

第二行N

个整数A[1],A[2],A[3].....A[N]。 ( 1A[i]231

)

接下来Q

行,每行一个操作。

  1. 1 x y 表示卿学姐会在x

号花坛种y

  • 朵花,并按相应的规律在后面的花坛上种花。

  • 2 x 表示沈宝宝问卿学姐第x

    1. 个花坛有多少朵花。

    数据保证:

    • 1N104


  • 1Q2106


  • x108

    x代表操作 2
  • 的询问下标

  • 对于操作 1

    , 1xN1y109

  • 对于操作 2

    , 1xN

    Output

    对于每个询问操作,按顺序输出答案对772002+233

    取模的值。

    Sample input and output

    Sample InputSample Output
    6 3
    1 2 3 2 1 2
    1 2 3
    2 3
    2 6
    5
    2
    

    Hint

    第一次种花会在第2

    号花坛种3朵,第3号花坛种2朵,第4号花坛种1朵,由于在第5号花坛不用种花,所以就不再继续种花了,最终每个花坛花的数量分别为1,5,5,3,1

    【来自CDOJ 每周一题 div2 题解】

    最简单的方法,O(N)去更新,然后O(1)去查询就好了,但是显然这样子会TLE的

    然后我们注意,我们发现这道保证查询操作的sigmax<=1e8

    所以我们把这个变成O(1)更新,O(N)查询就好了,这个东西打个延时标记就好了。

    比如1 x y

    我只需要使得lazy[x]+=y,表示x这个位置需要往下更新的大小增加y

    ed[x+y]++,表示某一个更新会在x+y这个位置停止。

    num[x]++,表示x这个位置多了一个更新。

    然后我们查询2 x的时候

    我们只需要从1这个位置,一直for到x这个位置就好了,然后处理我们刚才打上去的延迟标记。

    add表示现在累计了多少的值,Num表示现在我有多少个更新。

    add+=lazy[i],

    Num+=num[i],Num-=ed[i]。

    a[i] = (a[i]+add)%mod;

    add-=Num。显然走一步,就会减少Num

    然后就完了~

    然后有人会深入去思考,假设没有那个 sigma x<=1e8怎么办?

    其实查询和更新均摊一下就好了:

    有两种,

    1.分块,这个方法可以把查询和更新操作都均摊到O(sqrt(n)),直接暴力更新这个值在这个块内的数据,然后再暴力更新其他大块就好了

    2.线段树,直接暴力去怼线段树就好了

    【代码君1 暴力】

    【复杂度】O(1e8)

    
    //
    //Created by just_sort 2016/10/2 20:29
    //Copyright (c) 2016 just_sort.All Rights Reserved
    //
    
    #include <set>
    #include <map>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e4+5;
    const int mod = 772002+233;
    long long a[maxn],lazy[maxn],num[maxn],ed[maxn];
    int n,q;
    void update(int x,long long y)
    {
        lazy[x] += y;
        num[x]++;
        ed[min(1LL*n+1,1ll*x+y)]++;
    }
    long long query(int x)
    {
        long long add = 0;
        long long Num = 0;
        for(int i = 1; i <= x; i++)
        {
            add += lazy[i];
            Num += num[i];
            Num -= ed[i];
            lazy[i] = num[i] = ed[i] = 0;
            a[i] = (a[i] + add)%mod;
            add -= Num;
        }
        lazy[x+1] += add;
        num[x+1] += Num;
        return a[x];
    }
    
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i = 1; i <= n; i++) scanf("%lld",&a[i]),a[i]%=mod;
        for(int i = 1; i <= q; i++){
            int op,x;
            long long y;
            scanf("%d",&op);
            if(op == 1){
                scanf("%d%lld",&x,&y);
                update(x,y);
            }
            else{
                scanf("%d",&x);
                printf("%lld\n",query(x));
            }
        }
        return 0;
    }
    
    

    【代码君2 分块】

    【复杂度】O(qsqrt(n))

    //
    //Created by just_sort 2016/10/2 20:29
    //Copyright (c) 2016 just_sort.All Rights Reserved
    //
    
    #include <set>
    #include <map>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e4+5;
    const int mod = 772002+233;
    int num,block,belong[maxn];
    int l[maxn],r[maxn];
    long long a[maxn];
    long long lazy[maxn],number[maxn],ed[maxn];
    int n,q;
    void build()
    {
        block = sqrt(n);
        num = n/block;
        if(n % block) num++;
        for(int i = 1; i <= num; i++){
            l[i] = (i-1)*block+1,r[i] = i*block;
        }
        r[num] = n;
        for(int i = 1; i <= n; i++){
            belong[i] = (i-1)/block + 1;
        }
    }
    
    void update(int x,long long y)
    {
        for(int i = x; i <= r[belong[x]]; i++){
            a[i] = (a[i] + y)%mod;
            y--;
            if(y == 0) return ;
        }
        for(int i = belong[x] + 1; i <= num; i++){
            lazy[l[i]] += y;
            number[l[i]]++;
            if(y < (r[i]-l[i]+1)){
                ed[l[i]+y]++;
                break;
            }
            y -= (r[i]-l[i]+1);
        }
    }
    
    long long query(int x)
    {
        long long add = 0;
        long long Num = 0;
        for(int i = l[belong[x]]; i <= r[belong[x]]; i++){
            add += lazy[i];
            Num += number[i];
            Num -= ed[i];
            lazy[i] = number[i] = ed[i] = 0;
            a[i] = (a[i] + add)%mod;
            add -= Num;
        }
        return a[x]%mod;
    }
    
    int main()
    {
        scanf("%d%d",&n,&q);
        build();
        for(int i = 1; i <= n; i++){
            scanf("%lld",&a[i]);
            a[i]%=mod;
        }
        for(int i = 1; i <= q; i++){
            int op,x;
            long long y;
            scanf("%d",&op);
            if(op == 1){
                scanf("%d%lld",&x,&y);
                update(x,y);
            }
            else{
                scanf("%d",&x);
                printf("%lld\n",query(x));
            }
        }
        return 0;
    }
    

    【代码君3 线段树】

    【复杂度】O(QlogN)

    //
    //Created by just_sort 2016/10/2 20:29
    //Copyright (c) 2016 just_sort.All Rights Reserved
    //
    
    #include <set>
    #include <map>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e4+5;
    const int mod = 772002+233;
    struct node{
        int l,r;
        long long time,cnt;
    }Tree[maxn*4];
    long long a[maxn];
    void Build(int l,int r,int rt)
    {
        Tree[rt].l = l,Tree[rt].r = r;
        if(l == r){
            Tree[rt].time = Tree[rt].cnt = 0;
            return ;
        }
        int mid = (l+r)/2;
        Build(l,mid,rt*2);
        Build(mid+1,r,rt*2+1);
    }
    void update(int L,int R,long long v,int rt)
    {
        if(L > R) return ;
        if(L == Tree[rt].l && R == Tree[rt].r)
        {
            Tree[rt].time++;
            Tree[rt].cnt += v;
            return ;
        }
        int mid = (Tree[rt].l + Tree[rt].r)/2;
        update(L,min(mid,R),v,rt*2);
        update(max(L,mid+1),R,v-max(0,mid+1-L),rt*2+1);
    }
    
    long long query(int pos,int rt)
    {
        if(Tree[rt].l > pos || Tree[rt].r < pos) return 0;
        long long res = Tree[rt].cnt - (1LL*(pos - Tree[rt].l)*Tree[rt].time);
        if(Tree[rt].l != Tree[rt].r)
        {
            res += query(pos,rt*2) + query(pos,rt*2+1);
        }
        return res;
    }
    
    int main()
    {
        int n,q,l,r;
        scanf("%d%d",&n,&q);
        for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
        Build(1,n,1);
        for(int i = 1; i <= q; i++){
            int op,x;
            long long y;
            scanf("%d",&op);
            if(op == 1){
                scanf("%d%lld",&x,&y);
                l = x, r = x+(int)y-1;
                if(r >= n) r = n;
                update(l,r,y,1);
            }
            else{
                scanf("%d",&x);
                printf("%lld\n",(a[x] + query(x,1))%mod);
            }
        }
        return 0;
    }
    


### CDOJ 300 木杆上的蚂蚁 #### 题目描述 题目涉及若干只蚂蚁在一个长度为 \( L \) 的水平木杆上移动。每只蚂蚁初始位置和方向已知,当两只蚂蚁相遇时会立即掉头反向行走。目标是计算所有蚂蚁最终离开木杆的时间以及它们的顺序。 --- #### 解决方案概述 该问题的核心在于模拟蚂蚁的行为并处理碰撞事件。尽管表面上看起来需要复杂的碰撞检测逻辑,但实际上可以通过一种巧妙的方式简化问题:假设蚂蚁在碰撞时不改变方向,则可以忽略碰撞的影响[^2]。因此,只需关注每只蚂蚁到达木杆两端所需时间即可。 以下是解决问题的主要思路: 1. **输入解析** 输入数据包括测试用例数量 \( T \),每个测试用例包含木杆长度 \( L \) 和蚂蚁的数量 \( N \)。对于每只蚂蚁,记录其初始位置和移动方向(左或右)。 2. **时间和顺序计算** 对于每只蚂蚁: - 如果它朝左移动,则离木杆左侧的距离为其当前位置; - 如果它朝右移动,则离木杆右侧的距离为 \( L - \text{当前蚂蚁的位置} \)。 将这些距离存储下来,并按升序排列以确定蚂蚁离开木杆的顺序。 3. **输出结果** 输出每只蚂蚁离开木杆的时间及其编号。 --- #### Python 实现代码 以下是一个完整的 Python 实现: ```python t = int(input()) # 测试用例数量 for case in range(1, t + 1): n, l = map(int, input().split()) # 蚂蚁数量和木杆长度 ants = [] for _ in range(n): idx, pos, direction = input().split() idx = int(idx) pos = int(pos) if direction == 'L': time_to_fall = pos # 到达左边所需时间 else: time_to_fall = l - pos # 到达右边所需时间 ants.append((time_to_fall, idx)) # 按照掉落时间排序 sorted_ants_by_time = sorted(ants, key=lambda x: x[0]) # 提取原始索引以便后续匹配 original_indices = list(range(len(sorted_ants_by_time))) # 打印结果 print(f"Case #{case}:") for ant_index in original_indices: print(sorted_ants_by_time[ant_index][1], end=" ") print() ``` --- #### 关键点解释 1. **碰撞不影响总时间** 假设蚂蚁在碰撞时不改变方向,则整个过程中的最大时间为任意一只蚂蚁到最近端点的最大距离。这使得我们可以跳过复杂的状态更新操作[^2]。 2. **效率优化** 使用内置函数 `sorted` 可以高效完成排序任务,算法整体复杂度为 \( O(N \log N) \)。 3. **边界条件** 特殊情况包括仅有一只蚂蚁的情况或者所有蚂蚁都朝同一方向移动的情形。程序应能正确处理此类场景。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值