[分块]分块练习入门1~9(未完)

感谢@hzwer大佬出的练习题
题目链接LOJ
本蒟过弱,实在不知道怎么压缩代码量了->_->

数列分块入门 1

给出一个长为 n n 的数列,以及 n n 个操作,操作涉及区间加法,单点查值。


n n 个数,按照每 n \sqrt{n} 为一个块标记.

belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];

每次对所给的 [ l , r ] [l,r] 区间进行讨论,分为"单蹦"和"块",对于不满块的数,直接暴力修改.
对于满足块的数,直接打一个标记,当访问的时候再进行修改即可,类似于线段树 l a z y lazy .

/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/
const int MAXN = 1e5 + 10;

int belong[MAXN], tot = 1, a[MAXN], n, cnt;
struct block {
    int lazy;
    int start, end;
} p[MAXN];

int main() {
    cin >> n;
    cnt = sqrt(n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);  // cin >> a[i];
        belong[i] = tot;
        if (i % cnt == 1)
            p[tot].start = i;
        if (i % cnt == 0)
            p[tot++].end = i;
    }
    if (n % cnt) {
        p[tot].start = p[tot - 1].end + 1;
        p[tot++].end = n;
    }

    for (int i = 1; i <= n; i++) {
        int opt, l, r, c;
        scanf("%d %d %d %d", &opt, &l, &r, &c);  // cin >> opt>> l >> r >> c;
        if (opt)
            cout << p[belong[r]].lazy + a[r] << endl;
        else {
            if (belong[l] != belong[r]) {
                if (p[belong[l]].start == l)
                    p[belong[l]].lazy += c;
                else
                    for (int j = l; j <= p[belong[l]].end; j++) 
                    	a[j] += c;
                if (p[belong[r]].end == r)
                    p[belong[r]].lazy += c;
                else
                    for (int j = p[belong[r]].start; j <= r; j++) 
                    	a[j] += c;
                for (int j = belong[l] + 1; j < belong[r]; j++) 
                	p[j].lazy += c;
            } else if (p[belong[l]].start == l && p[belong[r]].end == r)
                p[belong[l]].lazy += c;
            else
                for (int j = l; j <= r; j++) 
                	a[j] += c;
        }
    }

    return 0;
}

数列分块入门 2

给出一个长为 n n 的数列,以及 n n 个操作,操作涉及区间加法,询问区间内小于某个值 x x 的元素个数。


区间加法仿照 1 1 即可.
对于每一个块内的数据,为了方便查询,我们分便对每一个块内的数据进行排序.
对于不满足块的数据,我们暴力处理,再将这个块内的数据排序,
满足块的区间,我们依然是打标记即可.
查询的时候,二分查找即可.

const int MAXN = 1e5 + 10;

/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/

int belong[MAXN], tot = 1, n, cnt, a[MAXN],b[MAXN];
struct block {
	int lazy;
	int start, end;
} p[MAXN];

void rechange(int l, int r)
{
	for(int i = l; i <= r; i++)
		b[i] = a[i];
	sort(b + l, b + r + 1);
}

int lowerbound(int *array, int size, int key, int lazy) {
	int first = 0, middle;
	int half, len;
	len = size;

	while (len > 0) {
		half = len >> 1;
		middle = first + half;
		if (array[middle] + lazy < key) {
			first = middle + 1;
			len = len - half - 1;  //在右边子序列中查找
		}
		else
			len = half;  //在左边子序列(包含middle)中查找
	}
	return first;
}

int main() {
	cin >> n;
	cnt = sqrt(n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		b[i]=a[i];
		belong[i] = tot;
		if (i % cnt == 1)
			p[tot].start = i;
		if (i % cnt == 0)
			p[tot++].end = i;
	}
	if (n % cnt) {
		p[tot].start = p[tot - 1].end + 1;
		p[tot++].end = n;
	}
	for (int i = 1; i < tot; i++) 
		sort(b + p[i].start, b + p[i].end + 1);

	for (int i = 1; i <= n; i++) {
		int opt, l, r, c;
		scanf("%d %d %d %d", &opt, &l, &r, &c);
		if (opt) {
			int ans = 0;
			c *= c;

			if (belong[l] != belong[r]) {
				if (p[belong[l]].start == l)
					ans += lowerbound(b + l, p[belong[l]].end - l + 1, c, p[belong[l]].lazy);
				else
					for (int j = l; j <= p[belong[l]].end; j++)
						if (a[j] + p[belong[j]].lazy < c)
							ans++;

				if (p[belong[r]].end == r)
					ans += lowerbound(b + p[belong[r]].start, r - p[belong[r]].start + 1, c, p[belong[r]].lazy);
				else
					for (int j = p[belong[r]].start; j <= r; j++)
						if (a[j] + p[belong[j]].lazy < c)
							ans++;

				for (int j = belong[l] + 1; j < belong[r]; j++)
					ans += lowerbound(b + p[j].start, p[j].end - p[j].start + 1, c, p[j].lazy);
			}
			else if (p[belong[l]].start == l && p[belong[r]].end == r)
				ans += lowerbound(b + l, r - l + 1, c, p[belong[l]].lazy);
			else 
				for (int j = l; j <= r; j++)
					if (a[j] + p[belong[j]].lazy < c)
						ans++;

			cout << ans << '\n';
		}
		else
			if (belong[l] != belong[r]) {
				if (p[belong[l]].start == l)
					p[belong[l]].lazy += c;
				else {
					for (int j = l; j <= p[belong[l]].end; j++)
						a[j] += c;
					rechange(p[belong[l]].start, p[belong[l]].end);
				}
				if (p[belong[r]].end == r)
					p[belong[r]].lazy += c;
				else {
					for (int j = p[belong[r]].start; j <= r; j++)
						a[j] += c;
					rechange(p[belong[r]].start, p[belong[r]].end);
				}
				for (int j = belong[l] + 1; j < belong[r]; j++)
					p[j].lazy += c;
			}
			else if (p[belong[l]].start == l && p[belong[r]].end == r)
				p[belong[l]].lazy += c;
			else {
				for (int j = l; j <= r; j++)
					a[j] += c;
				rechange(p[belong[l]].start, p[belong[l]].end);
			}
	}

	return 0;
}

数列分块入门 3

给出一个长为 n n 的数列,以及 n n 个操作,操作涉及区间加法,询问区间内小于某个值 x x 的前驱(比其小的最大元素)。


2 2 类似.

出题人的想法:
可以在块内维护其它结构使其更具有拓展性,比如放一个 set ,这样如果还有插入、删除元素的操作,会更加的方便。

const int MAXN = 1e5 + 10;

/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/

int belong[MAXN], tot = 1, n, cnt, a[MAXN], b[MAXN];
struct block {
	int lazy;
	int start, end;
} p[MAXN];

void rechange(int l, int r)
{
	for (int i = l; i <= r; i++)
		b[i] = a[i];
	sort(b + l, b + r + 1);
}

int lowerbound(int *array, int size, int key, int lazy) {
	int first = 0, middle;
	int half, len;
	len = size;

	while (len > 0) {
		half = len >> 1;
		middle = first + half;
		if (array[middle] + lazy < key) {
			first = middle + 1;
			len = len - half - 1;  //在右边子序列中查找
		}
		else
			len = half;  //在左边子序列(包含middle)中查找
	}
	return first;
}

int main() {
	cin >> n;
	cnt = sqrt(n);
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		b[i] = a[i];	//cin >> a[i];//scanf("%d", &a[i]);
		belong[i] = tot;
		if (i % cnt == 1)
			p[tot].start = i;
		if (i % cnt == 0)
			p[tot++].end = i;
	}
	if (n % cnt) {
		p[tot].start = p[tot - 1].end + 1;
		p[tot++].end = n;
	}
	for (int i = 1; i < tot; i++)
		sort(b + p[i].start, b + p[i].end + 1);

	for (int i = 1; i <= n; i++) {
		int opt, l, r, c;
		cin >> opt >> l >> r >> c;// scanf("%d %d %d %d", &opt, &l, &r, &c);
		if (opt) {
			int ans = -1;

			if (belong[l] != belong[r]) {
				if (p[belong[l]].start == l) {
					int t = lowerbound(b + l, p[belong[l]].end - l + 1, c, p[belong[l]].lazy);
					if (t)
						ans = max(ans, b[p[belong[l]].start+t-1] + p[belong[l]].lazy);
				}
				else
					for (int j = l; j <= p[belong[l]].end; j++)
						if (a[j] + p[belong[j]].lazy < c)
							ans = max(ans, a[j] + p[belong[j]].lazy);

				if (p[belong[r]].end == r) {
					int t = lowerbound(b + p[belong[r]].start, r - p[belong[r]].start + 1, c, p[belong[r]].lazy);
					if (t)
						ans = max(ans, b[p[belong[r]].start + t - 1] + p[belong[r]].lazy);
				}
				else
					for (int j = p[belong[r]].start; j <= r; j++)
						if (a[j] + p[belong[j]].lazy < c)
							ans = max(ans, a[j] + p[belong[j]].lazy);

				for (int j = belong[l] + 1; j < belong[r]; j++) {
					int t = lowerbound(b + p[j].start, p[j].end- p[j].start + 1, c, p[j].lazy);
					if (t)
						ans = max(ans, b[p[j].start + t - 1] + p[j].lazy);
				}
			}
			else if (p[belong[l]].start == l && p[belong[r]].end == r) {
				int t = lowerbound(b + l, r - l + 1, c, p[belong[l]].lazy);
				if (t)
					ans = max(ans, b[l + t - 1] + p[belong[l]].lazy);
			}
			else
				for (int j = l; j <= r; j++)
					if (a[j] + p[belong[j]].lazy < c)
						ans = max(ans, a[j] + p[belong[j]].lazy);

			cout << ans << '\n';
		}
		else
			if (belong[l] != belong[r]) {
				if (p[belong[l]].start == l)
					p[belong[l]].lazy += c;
				else {
					for (int j = l; j <= p[belong[l]].end; j++)
						a[j] += c;
					rechange(p[belong[l]].start, p[belong[l]].end);
				}
				if (p[belong[r]].end == r)
					p[belong[r]].lazy += c;
				else {
					for (int j = p[belong[r]].start; j <= r; j++)
						a[j] += c;
					rechange(p[belong[r]].start, p[belong[r]].end);
				}
				for (int j = belong[l] + 1; j < belong[r]; j++)
					p[j].lazy += c;
			}
			else if (p[belong[l]].start == l && p[belong[r]].end == r)
				p[belong[l]].lazy += c;
			else {
				for (int j = l; j <= r; j++)
					a[j] += c;
				rechange(p[belong[l]].start, p[belong[l]].end);
			}
	}

	return 0;
}

数列分块入门 4

给出一个长为 n n 的数列,以及 n n 个操作,操作涉及区间加法,区间求和。


求和预处理一下就阔以了,打标记的时候是长度 * 加法


typedef long long ll;
const int MAXN = 1e5 + 10;

/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/
ll cnt, a[MAXN];
int belong[MAXN], tot = 1, n;
struct block {
	ll lazy;
	ll sum;
	int start, end;
} p[MAXN];

int main() {
	cin >> n;
	cnt = sqrt(n);
	for (int i = 1; i <= n; i++) {
		cin >> a[i];	//scanf("%d", &a[i]);
		belong[i] = tot;
		p[tot].sum += a[i];
		if (i % cnt == 1)
			p[tot].start = i;
		if (i % cnt == 0)
			p[tot++].end = i;
	}
	if (n % cnt) {
		p[tot].start = p[tot - 1].end + 1;
		p[tot++].end = n;
	}

	for (int i = 1; i <= n; i++) {
		int opt, l, r;
		ll c;
		cin >> opt >> l >> r >> c;// scanf("%d %d %d %d", &opt, &l, &r, &c);
		if (opt) {
			ll ans = 0;

			if (belong[l] != belong[r]) {
				if (p[belong[l]].start == l)
					ans = (ans + (p[belong[l]].lazy*(p[belong[l]].end - p[belong[l]].start + 1)) % (c + 1) + p[belong[l]].sum % (c + 1)) % (c + 1);
				else
					for (int j = l; j <= p[belong[l]].end; j++)
						ans = (ans + a[j] % (c + 1) + p[belong[j]].lazy % (c + 1)) % (c + 1);

				if (p[belong[r]].end == r)
					ans = (ans + (p[belong[r]].lazy*(p[belong[r]].end - p[belong[r]].start + 1)) % (c + 1) + p[belong[r]].sum % (c + 1)) % (c + 1);
				else
					for (int j = p[belong[r]].start; j <= r; j++)
						ans = (ans + a[j] % (c + 1) + p[belong[j]].lazy % (c + 1)) % (c + 1);

				for (int j = belong[l] + 1; j < belong[r]; j++)
					ans = (ans + (p[j].lazy*(p[j].end - p[j].start + 1)) % (c + 1) + p[j].sum % (c + 1)) % (c + 1);
			}
			else if (p[belong[l]].start == l && p[belong[r]].end == r)
				ans = (ans + (p[belong[l]].lazy*(p[belong[l]].end - p[belong[l]].start + 1)) % (c + 1) + p[belong[l]].sum % (c + 1)) % (c + 1);
			else
				for (int j = l; j <= r; j++)
					ans = (ans + a[j] % (c + 1) + p[belong[j]].lazy % (c + 1)) % (c + 1);

			cout << ans << '\n';
		}
		else
			if (belong[l] != belong[r]) {
				if (p[belong[l]].start == l)
					p[belong[l]].lazy += c;
				else
					for (int j = l; j <= p[belong[l]].end; j++) {
						a[j] += c;
						p[belong[l]].sum += c;
					}
				if (p[belong[r]].end == r)
					p[belong[r]].lazy += c;
				else
					for (int j = p[belong[r]].start; j <= r; j++) {
						p[belong[r]].sum += c;
						a[j] += c;
					}
				for (int j = belong[l] + 1; j < belong[r]; j++)
					p[j].lazy += c;
			}
			else if (p[belong[l]].start == l && p[belong[r]].end == r)
				p[belong[l]].lazy += c;
			else
				for (int j = l; j <= r; j++) {
					a[j] += c;
					p[belong[l]].sum += c;
				}
	}

	return 0;
}

数列分块入门 5

给出一个长为 n n 的数列 ,以及 n n 个操作,操作涉及区间开方,区间求和。


这个题目其实比较搞人==
对于一个数,其属于 { 2 31 , 2 31 1 } \{-2^{31},2^{31}-1\} ,最多开方不超过 4 4 次.
还是和之前一样,单个暴力,整块标记.
对于一个块,如果开方次数超过 4 4 次,或者整个块只有 1 1 0 0 ,我们就可以认为不需要对其处理了,只记下和即可.
自己代码实现的时候,注意细节.

typedef long long ll;
const int MAXN = 1e5 + 10;

/* 
   belong[x]:元素x所在的块的编号,样例代码中为bl[x];
   start[x]:编号为x的块的最左边的点,样例代码中为st[x];
   end[x]:编号为x的块的最右边的点,样例代码中为ed[x]; */
int cnt, a[MAXN];
int belong[MAXN], tot = 1, n;
struct block {
  int lazy;
  ll sum;
  int start, end;
  bool f;
    block() {
    lazy = start = end = sum = 0;
    f = false;
}} p[MAXN];

void built() {
  cin >> n;
  cnt = sqrt(n);
  for (int i = 1; i <= n; i++) {
    cin >> a[i];                // scanf("%d", &a[i]);
    belong[i] = tot;

    if (a[i] != 0)
      p[tot].sum++;

    if (i % cnt == 1)
      p[tot].start = i;
    if (i % cnt == 0)
      p[tot++].end = i;
  }
  if (n % cnt) {
    p[tot].start = p[tot - 1].end + 1;
    p[tot++].end = n;
  }
}

void print(int l, int r) {
  ll ans = 0, c;
  cin >> c;

  if (belong[l] != belong[r]) {
    if (p[belong[l]].start == l)
      if (p[belong[l]].lazy > 4 || p[belong[l]].f)
        ans += p[belong[l]].sum;

      else {
        p[belong[l]].f = true;
        for (int i = p[belong[l]].start; i <= p[belong[l]].end; i++) {
          int lazy = p[belong[l]].lazy, x = a[i];
          while (lazy) {
            if (x == 0 || x == 1)
              break;
            x = sqrt(x);
            lazy--;
          }
          if (x != 1 && x != 0)
            p[belong[l]].f = false;
          ans += x;
        }
    } else
      for (int i = l; i <= p[belong[l]].end; i++) {
        int lazy = p[belong[l]].lazy, x = a[i];
        while (lazy) {
          if (x == 0 || x == 1)
            break;
          x = sqrt(x);
          lazy--;
        }
        ans += x;
      }

    if (p[belong[r]].end == r)
      if (p[belong[r]].lazy > 4 || p[belong[r]].f)
        ans += p[belong[r]].sum;

      else {
        p[belong[r]].f = true;
        for (int i = p[belong[r]].start; i <= p[belong[r]].end; i++) {
          int lazy = p[belong[r]].lazy, x = a[i];
          while (lazy) {
            if (x == 0 || x == 1)
              break;
            x = sqrt(x);
            lazy--;
          }

          if (x != 1 && x != 0)
            p[belong[r]].f = false;
          ans += x;
        }
    } else
      for (int i = p[belong[r]].start; i <= r; i++) {
        int lazy = p[belong[r]].lazy, x = a[i];
        while (lazy) {
          if (x == 0 || x == 1)
            break;
          x = sqrt(x);
          lazy--;
        }
        ans += x;
      }

    for (int i = belong[l] + 1; i < belong[r]; i++)
      if (p[i].lazy > 4 || p[i].f)
        ans += p[i].sum;
      else {
        p[i].f = true;
        for (int j = p[i].start; j <= p[i].end; j++) {
          int lazy = p[i].lazy, x = a[j];
          while (lazy) {
            if (x == 0 || x == 1)
              break;
            x = sqrt(x);
            lazy--;
          }

          if (x != 1 && x != 0)
            p[i].f = false;
          ans += x;
        }
      }
  }

  else if (p[belong[l]].start == l && p[belong[r]].end == r)
    if (p[belong[l]].lazy > 4 || p[belong[l]].f)
      ans += p[belong[l]].sum;
    else {
      p[belong[l]].f = true;
      for (int i = p[belong[l]].start; i <= p[belong[l]].end; i++) {
        int lazy = p[belong[r]].lazy, x = a[i];
        while (lazy) {
          if (x == 0 || x == 1)
            break;
          x = sqrt(x);
          lazy--;
        }

        if (x != 1 && x != 0)
          p[belong[l]].f = false;
        ans += x;
      }
    }

  else
    for (int i = l; i <= r; i++) {
      int lazy = p[belong[l]].lazy, x = a[i];
      while (lazy) {
        if (x == 0 || x == 1)
          break;
        x = sqrt(x);
        lazy--;
      }
      ans += x;
    }

  cout << ans << '\n';
}

void update(int l, int r) {
  ll c;
  cin >> c;
  if (belong[l] != belong[r]) {
    if (p[belong[l]].start == l)
      p[belong[l]].lazy++;
    else
      for (int i = l; i <= p[belong[l]].end; i++)
        a[i] = sqrt(a[i]);
    if (p[belong[r]].end == r)
      p[belong[r]].lazy++;
    else
      for (int i = p[belong[r]].start; i <= r; i++)
        a[i] = sqrt(a[i]);
    for (int i = belong[l] + 1; i < belong[r]; i++)
      p[i].lazy++;
  } else if (p[belong[l]].start == l && p[belong[r]].end == r)
    p[belong[l]].lazy++;
  else
    for (int i = l; i <= r; i++)
      a[i] = sqrt(a[i]);
}

int main() {
  built();
  for (int i = 1; i <= n; i++) {
    int opt, l, r;
    cin >> opt >> l >> r;       // scanf("%d %d %d %d",
    // &opt,
    // &l, &r, &c);
    if (opt)
      print(l, r);
    else
      update(l, r);
  }

  return 0;
}

数列分块入门 6

给出一个长为 4n$ 的数列,以及 n n 个操作,操作涉及单点插入,单点询问,数据随机生成.


到了喜闻乐见的动态分块了 23333 23333 .
c + + c++ v e c t o r vector 大法好,我是不会用指针写链表的,拒绝
每次插入一个数,就找到对应的块,扔进去就行.
将插入的次数记下来,当次数超过 n \sqrt n 的时候就进行重构,也就是重新分块.
然后就没有然后了.

const int MAXN = 1e6 + 10;
const int INF = 1e8 + 10;
const int MOD = 998244353;
const int ans = 11;
typedef long long ll;

int a[MAXN];
vector<int>p[MAXN];
int n, tot = 0, m, optt;

void find(int k)
{
	int num = 0;
	for (int i = 0; i < tot; i++) {
		num += p[i].size();
		if (num >= k) {
			num -= p[i].size();
			k = k - num - 1;
			cout << p[i][k] << '\n';
			break;
		}
	}
}

void rebuild()
{
	int num = 0;
	for (int i = 0; i < tot; i++) {
		for (int j = 0; j < p[i].size(); j++)
			a[++num] = p[i][j];
		p[i].clear();
	}

	n = num, m = sqrt(n), tot = 0;
	for (int i = 1; i <= n; i++) {
		p[tot].push_back(a[i]);
		if (i%m == 0)
			tot++;
	}
	if (n%m != 0)
		tot++;
}

void insert(int k, int x)
{
	int num = 0;
	for (int i = 0; i < tot; i++) {
		num += p[i].size();
		if (num >= k) {
			num -= p[i].size();
			k = k - num - 1;
			p[i].insert(p[i].begin() + k, x);
			optt++;
			break;
		}
	}
	if (optt == m) {
		rebuild();
		optt = 0;
	}
}

int main()
{
	cin >> n;
	m = sqrt(n);
	for (int i = 1; i <= n; i++) {
		int x;
		scanf("%d",&x);
		p[tot].push_back(x);
		if (i%m == 0)
			tot++;
	}
	if (n%m != 0)
		tot++;

	int opt, l, r, c, q = n;
	for (int i = 1; i <= q; i++) {
		scanf("%d%d%d%d",&opt,&l,&r,&c);
		if (opt)
			find(r);
		else
			insert(l, r);
	}

	return 0;
}
发布了62 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/UnKfrozen/article/details/97779628