首页 > 试题广场 >

序列维护

[编程题]序列维护
  • 热度指数:6377 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
小易在维护数据的时候遇到一个需求,具体来说小易有一系列数据,这些数据了构成一个长度为n的数字序列,接下来小易会在这个序列上进行q次操作。
每次操作有一个查询的数字x,小易需要将序列数据中所有大于等于x的数字都减一,并输出在本次操作中有多少个数字被减一了。
小易犯了难,希望你能帮帮他。

输入描述:
第一行n,q,表示数字个数和操作个数。 
接下来一行n个数表示初始的数字。
接下来q行,每行一个数,表示指定的数字x。


输出描述:
对于每个询问,输出一个数字表示答案
示例1

输入

4 3
1 2 3 4
4
3
1

输出

1
2
4
示例2

输入

3 2  
1 2 3    
3  
3

输出

1
0

思路

  • 暴力解法
    暴力解法很容易想到,直接按题目说的来做就可以了
  • 优化
    这个数据量,显然需要用O(nlogn)或者O(n)算法,则会想到排序,如果从大到小排,那么每次查询一个数字x,使得大于等于x的数字都会-1,那么数列还是有序的。也就是数列始终都是有序的,这样就可以进行剪枝了,遍历到小于x的直接break跳出循环即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int a[N], hs[N];
int n, q, x;

int main() {
    scanf("%d%d", &n, &q);
    int x;
    for (int i = 1; i <= n; ++ i) {
        scanf("%d", &a[i]);
    }
    sort(a + 1, a + n + 1, greater<int>());

    while (q -- ) {
        scanf("%d", &x);
        int ret = 0;
        for (int i = 1; i <= n; ++ i) {
            if (a[i] >= x) {
                a[i] -= 1;
                ret ++ ;
            } else {
                break;
            }
        }
        printf("%d\n", ret);
    }
    return 0;
}

发表于 2020-07-30 18:19:16 回复(1)

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int q = scanner.nextInt();
        int[] arr = new int[n];
        //将数字录入数组
        for (int i = 0; i < n; i++) {
            int num = scanner.nextInt();
            arr[i] = num;
        }
        //先将数组排序
        Arrays.sort(arr);
        //查询次数
        for (int i = 0; i < q; i++) {
            //需要查询的数字
            int x = scanner.nextInt();
            System.out.println(demo4(arr, x));
        }
    }

    public static int demo4(int[] arr, int x) {
        int count = 0;
        //从大往小比较,碰到小于x的及时终止循环,能优化时间
        for (int i = arr.length-1; i >= 0; i--) {
            if (arr[i] >= x) {
                arr[i]--;
                count++;
            } else {
                break;
            }
        }
        return count;
    }
}

编辑于 2020-04-06 16:21:20 回复(1)
线段树+二分,O(n (logn)^2)
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int N=2e5+5;
const double eps=1e-8;
const double PI = acos(-1.0);
#define lowbit(x) (x&(-x))
int sum[N<<2],add[N<<2];
int a[N];
void pushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=a[l];
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushUp(rt);
}
void pushDown(int rt,int ln,int rn)
{
    if(add[rt])
    {
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=add[rt]*ln;
        sum[rt<<1|1]+=add[rt]*rn;
        add[rt]=0;
    }
}
void update(int L,int R,int C,int l,int r,int rt)
{
    if(L <= l && r <= R)
    {
        sum[rt]+=C*(r-l+1);
        add[rt]+=C;
        return ;
    }
    int m=(l+r)>>1;
    pushDown(rt,m-l+1,r-m);
    if(L <= m) update(L,R,C,l,m,rt<<1);
    if(R >  m) update(L,R,C,m+1,r,rt<<1|1);
    pushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L <= l && r <= R)
    {
        return sum[rt];
    }
    int m=(l+r)>>1;
    pushDown(rt,m-l+1,r-m);
    int ans=0;
    if(L <= m) ans+=query(L,R,l,m,rt<<1);
    if(R >  m) ans+=query(L,R,m+1,r,rt<<1|1);
    return ans;
}
int main()
{
    std::ios::sync_with_stdio(false);
    int n,q;
    while(cin>>n>>q)
    {
        memset(add,0,sizeof(add));
        for(int i=1; i<=n; i++)
        {
            cin>>a[i];
        }
        sort(a+1,a+1+n);
        build(1,n,1);
        while(q--)
        {
            int l=1,r=n,m,ans=-1,x;
            cin>>x;
            while(l<=r)
            {
                m=(l+r)>>1;
                if(query(m,m,1,n,1)>=x)
                {
                    r=m-1;
                    ans=m;
                }
                else
                {
                    l=m+1;
                }
            }
            if(ans==-1)
            {
                cout<<0<<endl;
            }
            else
            {
                cout<<n-ans+1<<endl;
                update(ans,n,-1,1,n,1);
            }
        }
    }
    return 0;
}


编辑于 2020-04-04 20:47:09 回复(0)
n = list(map(int, input().split(' ')))
len = n[0]
N = n[1]
data = list(map(int, input().split(' ')))
while N:
    N = N-1
    num = 0
    val = int(input())
    for i in range(len):
        if(data[i] >= val):
            data[i] = data[i] - 1
            num = num+1
    print(num)

发表于 2020-02-19 22:23:31 回复(4)
c = input('数字个数和操作次数:')
d = c.split(' ')
n,q = int(d[0]),int(d[1])


a = input('请输入初始数:')
b = a.split(' ', n)
list1 = []
for i in b:
    list1.append(int(i))


list2 = []
while q:
    x = int(input('指定数字:'))
    list2.append(x)
    q -= 1
    


for k in list2:
    list3 = []
    count = 0
    for j in list1:
        if j >= k:
            list3.append(j-1)
            count += 1
        else:
            list3.append(j)
    list1 = list3.copy()
    print('有' + str(count) + '个数字被减一。')
我调试了好久,终于写出来了。
发表于 2020-03-14 16:48:14 回复(4)
常规套路,先对数组排序后再从后往前进行查找
import java.util.Scanner;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), q = sc.nextInt();
        int[] arr = new int[n];
        for(int i = 0; i < n; i++) arr[i] = sc.nextInt();
        Arrays.sort(arr);     // 对数组按升序排列,防止之后每次查找都需要遍历一遍数组
        // 开始q次减一操作
        for(int i = 0; i < q; i++){
            int opNum = sc.nextInt();
            System.out.println(solve(arr, opNum));
        }
    }
    
    private static int solve(int[] arr, int q) {
        int count = 0;
        // 从后往前遍历不会浪费查找
        for(int i = arr.length - 1; i >= 0; i--){
            if(arr[i] >= q){
                arr[i] -= 1;
                count ++;
            }else
                break;     // 遇到小于q的数直接退出循环
        }
        return count;
    }
}


发表于 2020-10-23 11:29:45 回复(0)
import sys
b = []#b存储第三行及之后的数据
n,p = map(int,sys.stdin.readline().strip().split())
a = list(map(int,sys.stdin.readline().strip().split())) #a存储第二行数据
for i in range(p):
    b.append(int(sys.stdin.readline().strip()))

for i in range(p):
    k = 0
    for j in range(n):
        if a[j] >= b[i]:
            a[j] -= 1
            k += 1
    print(k)

本地跑通,不知道为什么平台上不行



编辑于 2020-03-21 16:30:52 回复(0)
# 好笨的方法,别笑话我,加油

n = int(input("n = "))
q = int(input("q = "))
list1 = [i for i in range(1,n+1)]
for i in range(q):
    x = int(input("第" + str(i+1) + "次输入:"))
    num = 0
    list2 = []
    for c in list1:
        if c >= x:
            list2.append(c-1)
            num = num + 1
        else:
            list2.append(c)
    print("本次操作中有{}个数被减一!".format(num))
    list1 = list2.copy()



发表于 2020-03-04 20:15:59 回复(0)
二分 + 线段树区间更新 模板题
#include <bits/stdc++.h>

using namespace std;

class segtree{
public:
  int n;
  int* data;
  int* tree;
  int* lazy;

  segtree(int* arr, int _n) : n(_n) {
    data = new int[_n];
    tree = new int[_n * 4];
    lazy = new int[_n * 4];
    fill_n(tree, _n * 4, 0);
    fill_n(lazy, _n * 4, 0);
    for (int i = 0; i < _n; i++) {
      data[i] = arr[i];
    }
    build(0, 0, n - 1);
  }

  ~segtree() {
    delete []data;
    delete []tree;
    delete []lazy;
  }

  void push(int tId) {
    tree[tId] = tree[tId * 2 + 1] + tree[tId * 2 + 2];
  }

  void pull(int tId, int l, int r) {
    int mid = (l + r) / 2;
    tree[tId * 2 + 1] += (mid - l + 1) * lazy[tId];
    tree[tId * 2 + 2] += (r - mid) * lazy[tId];
    lazy[tId * 2 + 1] += lazy[tId];
    lazy[tId * 2 + 2] += lazy[tId];
    lazy[tId] = 0;
  }

  void build(int tId, int l, int r) {
    if (l == r) {
      tree[tId] = data[l];
      return;
    }
    int mid = (l + r) / 2;
    build(tId * 2 + 1, l, mid);
    build(tId * 2 + 2, mid + 1, r);
    push(tId);
  }

  int get(int tId, int l, int r, int x) {
    if (l == r && l == x) {
      return tree[tId];
    }
    if (lazy[tId]) {
      pull(tId, l, r);
    }
    int mid = (l + r) / 2;
    if (x <= mid) {
      return get(tId * 2 + 1, l, mid, x);
    } else {
      return get(tId * 2 + 2, mid + 1, r, x);
    }
  }

  void modify(int tId, int l, int r, int ml, int mr, int v) {
    if (ml <= l && r <= mr) {
      tree[tId] += (r - l + 1) * v;
      lazy[tId] += v;
      return;
    }
    if (lazy[tId] != 0) {
      pull(tId, l, r);
    }
    int mid = (l + r) >> 1;
    if (mr <= mid) {
      modify((tId * 2) + 1, l, mid, ml, mr, v);
    } else if (ml > mid) {
      modify((tId * 2) + 2, mid + 1, r, ml, mr, v);
    } else {
      modify((tId * 2) + 1, l, mid, ml, mid, v);
      modify((tId * 2) + 2, mid + 1, r, mid + 1, mr, v);
    }
    push(tId);
  }
};

int arr[234567];

int main() {
  int n, q;
  cin >> n >> q;
  for (int i = 0; i < n; i++) {
    scanf("%d", &arr[i]);
  }
  sort(arr, arr + n);
  segtree st(arr, n);
  while (q--) {
    int x;
    int l = 0, r = n - 1;
    scanf("%d", &x);
    while (l <= r) {
      int mid = (l + r) >> 1;
      if (st.get(0, 0, n - 1, mid) < x) l = mid + 1;
      else r = mid - 1;
    }
    if (l >= n) {
      cout << "0" << "\n";
    } else {
      cout << n - 1 - l + 1 << "\n";
      st.modify(0, 0, n - 1, l, n - 1, -1);
    }
  }
  return 0;
}

发表于 2020-09-12 11:10:26 回复(0)
为什么好多用暴力的 ..... 线段树解法 :
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

class SegmentTree {
  vector<int> data;

  struct TreeNode {
    int sum;
    int add;
  };

  vector<TreeNode> tree;

 public:
  template <typename T>
  SegmentTree(T&& data) : data(forward<T>(data)) {
    int n = this->data.size();
    tree.resize(n * 4);
    build(0, 0, n - 1);
  }

 private:
  // [l,r]表示idx掌管的区间
  void build(int idx, int l, int r) {
    if (l == r) {
      tree[idx].sum = data[l];
      tree[idx].add = 0;
    } else {
      int mid = getMiddle(l, r);
      // 递归左右区间
      build(childLeft(idx), l, mid);
      build(childRight(idx), mid + 1, r);
      // 左右区间都建立好了,更新idx即可
      update(idx);
    }
  }

  /*
  idx表示当前树节点的索引
  l,r表示idx掌管的区间
  s,t表示要获取[l,r]区间在[s,t]范围内的和
  */
  int query(int idx, int l, int r, int s, int t) {
    if (s <= l && r <= t) {
      // 直接返回即可
      return tree[idx].sum;
    }
    pushdown(idx, l, r);
    int mid = getMiddle(l, r), ans = 0;
    if (mid >= s) ans += query(childLeft(idx), l, mid, s, t);
    if (mid < t) ans += query(childRight(idx), mid + 1, r, s, t);
    return ans;
  }

  /*
  idx表示当前树节点的索引
  l,r表示idx掌管的区间
  s,t表示要在[s,t]上都加上一个数
  */
  void add(int idx, int l, int r, int s, int t, int val) {
    if (s <= l && r <= t) {
      // 直接加上即可
      tree[idx].add += val;
      tree[idx].sum += val * (r - l + 1);
      return;
    }
    pushdown(idx, l, r);
    int mid = getMiddle(l, r);
    if (mid >= s) add(childLeft(idx), l, mid, s, t, val);
    if (mid < t) add(childRight(idx), mid + 1, r, s, t, val);
    update(idx);
  }

  // 使得tree[idx].sum是正确的
  void update(int idx) {
    tree[idx].sum = tree[childLeft(idx)].sum + tree[childRight(idx)].sum;
  }

  // 将tree[idx].add传递给左右节点,确保左右节点的add是正确的
  void pushdown(int idx, int l, int r) {
    auto& root = tree[idx];
    int mid = getMiddle(l, r);
    int lc = childLeft(idx);
    int rc = childRight(idx);

    auto &lr = tree[lc], &rr = tree[rc];

    lr.sum += root.add * (mid - l + 1);
    lr.add += root.add;

    rr.sum += root.add * (r - mid);
    rr.add += root.add;

    root.add = 0;
  }

  // 获取左子节点
  static int childLeft(int index) { return index * 2 + 1; }
  // 获取右子节点
  static int childRight(int index) { return index * 2 + 2; }
  // 平分[l,r]为[l,mid] , [mid+1,r] , 返回mid
  static int getMiddle(int l, int r) { return l + (r - l) / 2; }

 public:
  int get(int idx) { return query(0, 0, data.size() - 1, idx, idx); }

  // 在[l,r]区间加上某个值
  void add(int l, int r, int val) { add(0, 0, data.size() - 1, l, r, val); }
};

int main() {
  int n, q, x;
  cin >> n >> q;
  vector<int> a(n);
  for (auto& v : a) cin >> v;
  sort(a.begin(), a.end());
  SegmentTree tree(move(a));
  // 线段树+二分
  while (q--) {
    cin >> x;
    auto deal = [&]() {
      if (tree.get(n - 1) < x) return 0;
      // 查询>=x的最小下标
      // 二分查找满足条件的最小值,lr左闭右开
      int l = 0, r = n;
      while (l + 1 < r) {
        int mid = l + ((r - l) >> 1);
        tree.get(mid) >= x ? r = mid : l = mid;
      }
      if (r == 1 && tree.get(0) >= x) --r;
      tree.add(r, n - 1, -1);
      // cout << "find : " << r << endl;
      return n - r;
    };
    cout << deal() << endl;
    // cout << "seq : ";
    // for (int i = 0; i < n; ++i) cout << tree.get(i) << ' ';
    // cout << endl;
  }
  return 0;
}



发表于 2022-07-06 16:18:38 回复(0)
随机生成几组数试了一下
import random import numpy as np
n=random.randint(1,20)
x=np.random.randint(1,10,random.randint(1,10))
s=np.random.randint(1,20,n) for i in x: print(len(s[s>=i]))
     s=np.where(s>=i,s-1,s)

发表于 2022-04-29 18:35:33 回复(0)
n,q=map(int,input().split())
arr=list(map(int,input().split()))
arr.sort()
flag=0
for i in range(q):
    x=int(input())
    if x in arr:
        t=arr.index(x)
        arr[t:n]=[m-1 for m in arr[t:n]]
        print(n-t)
    else:
        for j in range(n):
            if arr[j]>=x:
                arr[j:n]=[m-1 for m in arr[j:n]]
                flag=1
                print(n-j)
                break
        
        if flag==0:
            print(0)


为什么一直有这个错误x=int(input()) EOFError: EOF when reading a line
发表于 2021-04-10 11:59:03 回复(0)
class Solution:
    def num_of_minus(self, array, x):
        count = 0 
        for i in range(len(array)):
            if array[i]>=x:
                count = count+1
                array[i]-=x
        print(count)
        return array

if __name__ == '__main__':
    lin1 = (input('数字个数和操作次数:')).split(' ')
    n,q = int(lin1[0]),int(lin1[1])

    line2 = (input('请输入初始数:')).split(' ', n)
    array = [int(e) for e in line2]

    listQ = [int(input("指定数字:")) for i in range(q)]

    for x in listQ:
        array = Solution().num_of_minus(array,x)

发表于 2021-03-18 20:49:59 回复(0)
为什么每次执行方法不再排序?不怕-1操作后不是发生至少1的乱序?
发表于 2020-11-06 14:12:46 回复(0)
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int n,q,pos,x;
    cin>>n>>q;
    int*a=new int[n];
    for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
    while(q-->0){
        cin>>x;
        pos=lower_bound(a,a+n,x)-a;
        cout<<n-pos<<endl;
        for(;pos<n;pos++) a[pos]--;
    }
    return 0;
}


发表于 2020-09-12 00:39:24 回复(0)
nq = input().split(" ")
n = int(nq[0])
q = int(nq[1])
 
def juge(x,datalist):
    rst = 0
    for i in range(len(datalist)):
        if datalist[i] >= x:
            rst += 1
            datalist[i] = datalist[i]-1
    return rst,datalist
 
result = []
sn = list(map(int,input().split(" ")))
for i in range(q):
    x = int(input().split(" ")[0])
    rst,sn = juge(x,sn)
    result.append(rst)
 
for j in result:
    print(j)

求解答,我哪里出问题了?感觉在自己的IDE能正常运行呀
发表于 2020-09-05 11:03:54 回复(0)
本地用pycharm编辑器运行完全没问题,到了这就是报错,哪位大佬给我看看,怎么解决这个问题
发表于 2020-08-29 10:29:15 回复(2)
全部通过,二分修正版,直接算出大于等于值的个数,代码执行速度特别快,代码特别精简
import java.util.Arrays;
import java.util.Scanner;

public class Main{
	static int search(int[] array,int value) {
		int left=0;
		int right=array.length-1;
		int mid=0;
		while(left<=right) {
			mid=(left+right)>>1;
			if(value<=array[mid])
				right=mid-1;
			else
				left=mid+1;
		}
		return left<array.length?left:array.length;
	}
	public static void main(String[] args){
		Scanner scanner=new Scanner(System.in);
		int n=scanner.nextInt();
		int q=scanner.nextInt();
		int[] a=new int[n];
		for(int i=0;i<n;i++) 
			a[i]=scanner.nextInt();
		Arrays.sort(a);
		for(int i=0;i<q;i++) {
			int x=scanner.nextInt();
			int ind=search(a, x);
			System.out.println(n-ind);
			for(int j=ind;j<n;j++)
				a[j]--;
		}
	}
}

发表于 2020-08-12 13:05:01 回复(0)
while(line1 = readline()){
    let line1Arr = line1.split(' ');
    let n = parseInt(line1Arr[0]);
    let q = parseInt(line1Arr[1]);
     
    let arr = readline().split(' ').map((it)=>parseInt(it));
    for(let i=0;i<q;i++){
        let x = parseInt(readline());
        let count = 0;
        arr.forEach((val,index)=>{
            if(val >= x){
                count++;
                arr[index] = --val;
            }
        })
        print(count);
    }
}
用js总是通不过是为什么啊,我实在是看不出有什么问题??

发表于 2020-08-09 12:09:06 回复(0)
排序了还是过不了,python无爱了。。。
n,q = list(map(int, input().split(' ')))
if n != '':
    nums = sorted(list(map(int, input().split(' '))),reverse=True)
    for _ in range(q):
        x = int(input())
        res = 0
        for i in range(n):
            if nums[i] >= x:
                nums[i] -= 1
                res += 1
            else:
                break
        print(res)


发表于 2020-08-08 11:51:24 回复(0)