牛客网暑期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].

扫描二维码关注公众号,回复: 2835022 查看本文章

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<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
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<x1[i].x)
					continue;
				ans[x2[j].t] = (ans[x2[j].t]+x1[i].y*C[k+x2[j].x-x1[i].x][k]%mod)%mod;
			}
		}
	}
	CDQ(L, M);
	CDQ(M+1, R);
}
int main(void)
{
	int i, j;
	for(i=0;i<=200002;i++)
		C[i][0] = 1;
	for(i=1;i<=200002;i++)
	{
		for(j=1;j<=42;j++)
			C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
	}
	memset(ans, -1, sizeof(ans));
	scanf("%d%d%d", &n, &m, &k);
	k -= 1;
	for(i=1;i<=m;i++)
	{
		s[i].t = i;
		scanf("%d", &s[i].op);
		s[i].op ^= 1;
		if(s[i].op)
			scanf("%lld%lld", &s[i].x, &s[i].y);
		else
		{
			scanf("%d", &s[i].x);
			ans[i] = 0;
		}
	}
	CDQ(1, m);
	for(i=1;i<=m;i++)
	{
		if(ans[i]!=-1)
			printf("%lld\n", ans[i]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/81745101
今日推荐