牛客网暑期ACM多校训练营(第九场)H. Prefix Sum(CDQ分治)

题目描述

Niuniu has learned prefix sum and he found an interesting about prefix sum.

Let's consider (k+1) arrays a[i] (0 <= i <= k)

The index of a[i] starts from 1. 

a[i] is always the prefix sum of a[i-1]. 

"always" means a[i] will change when a[i-1] changes.

"prefix sum" means a[i][1] = a[i-1][1] and a[i][j] = a[i][j-1] + a[i-1][j] (j >= 2)

 

Initially, all elements in a[0] are 0.

There are two kinds of operations, which are modify and query.

For a modify operation, two integers x, y are given, and it means a[0][x] += y.

For a query operation, one integer x is given, and it means querying a[k][x].

As the result might be very large, you should output the result mod 1000000007.

输入描述:

The first line contains three integers, n, m, k.
n is the length of each array.
m is the number of operations.
k is the number of prefix sum.

In the following m lines, each line contains an operation.
If the first number is 0, then this is a change operation.
There will be two integers x, y after 0, which means a[0][x] += y;
If the first number is 1, then this is a query operation.
There will be one integer x after 1, which means querying a[k][x].

1 <= n <= 100000
1 <= m <= 100000
1 <= k <= 40
1 <= x <= n
0 <= y < 1000000007

输出描述:

For each query, you should output an integer, which is the result.

输入

4 11 3
0 1 1
0 3 1
1 1
1 2
1 3
1 4
0 3 1
1 1
1 2
1 3
1 4

输出

1
3
7
13
1
3
8
16

 

题意:给你一个(k+1)*n的矩阵,初始全为0,m个操作

操作①0 x y表示将a[0][x] += y,并且计算∀(i∈[1,k],j∈[1,n]),a[i][j] = a[i-1][j]+a[i][j-1]

操作②1 x 表示询问a[k][x]的值

 

很容易算出:a[0][x]对a[k][y]的贡献为\small C_{k-1+y-x}^{k-1}\textrm{},因为k的范围很小所以可以O(1)预处理所有组合数

用上面的结论,直接暴力每个操作对后面所有询问的贡献,复杂度是O(n*m)的,肯定会超时

对于这种每次操作对后面询问产生特定贡献的题,考虑对询问时间进行CDQ分治

 

步骤如下:

  1. 按照操作的时间进行排序(其实根本不用排,因为本来就有序,输入顺序就是了)
  2. 对操作进行二分,假设当前二分区间为[L, R],一个很显然的结论是对于[L, m]的所有修改操作,一定对(m, R]区间中的询问操作有效
  3. 所以对于每个二分区间[L, R],只需要计算[L, m]中的所有修改操作对(m, R]区间中所有询问操作的贡献即可
  4. 如果区间长度len≤2000,直接暴力所有操作对所有询问的贡献,复杂度O(len²)
  5. 如果区间长度len>2000,将所有操作全部加到矩阵中,然后暴力计算出矩阵所有位置的值,最后对于所有询问直接查矩阵对应位置(这里可以优化:不用暴力整个矩阵的值,只需要用组合数计算最后一排的值即可)复杂度O(nk)或O(n)
  6. 分析下整体复杂度:len>2000的递归次数最坏情况下(n=m=100000)约为15次,所以这部分复杂度为O(15n), len<2000的递归次数约为m次,每次复杂度均摊log²(m)次,所以这部分复杂度O(mlogm),整体复杂度O(15n+mlog²m)
  7. 搞定!

 

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define mod 1000000007
typedef struct Res
{
	LL x, y;
	int t, op;
}Res;
Res s[200005], x1[200005], x2[200005];
int n, m, k;
LL ans[200005], p[45][200005], C[200005][45];
void CDQ(int L, int R)
{
	int M, i, j, a, b;
	M = (L+R)/2;
	if(L>=R)
		return;
	if(M-L+1>=1000)
	{
		for(i=0;i<=n;i++)
			p[0][i] = 0;
		for(i=L;i<=M;i++)
		{
			if(s[i].op==1)
				p[0][s[i].x] = (p[0][s[i].x]+s[i].y)%mod;
		}
		for(i=1;i<=k+1;i++)
		{
			for(j=1;j<=n;j++)
				p[i][j] = (p[i-1][j]+p[i][j-1])%mod;
		}
		for(i=M+1;i<=R;i++)
		{
			if(s[i].op==0)
				ans[s[i].t] = (ans[s[i].t]+p[k+1][s[i].x])%mod;
		}
	}
	else
	{
		a = b = 0;
		for(i=L;i<=M;i++)
		{
			if(s[i].op==1)
				x1[++a] = s[i];
		}
		for(i=M+1;i<=R;i++)
		{
			if(s[i].op==0)
				x2[++b] = s[i];
		}
		for(i=1;i<=a;i++)
		{
			for(j=1;j<=b;j++)
			{
				if(x2[j].x

 

你可能感兴趣的:(#,分治与分块)