题目描述
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]的贡献为,因为k的范围很小所以可以O(1)预处理所有组合数
用上面的结论,直接暴力每个操作对后面所有询问的贡献,复杂度是O(n*m)的,肯定会超时
对于这种每次操作对后面询问产生特定贡献的题,考虑对询问时间进行CDQ分治
步骤如下:
- 按照操作的时间进行排序(其实根本不用排,因为本来就有序,输入顺序就是了)
- 对操作进行二分,假设当前二分区间为[L, R],一个很显然的结论是对于[L, m]的所有修改操作,一定对(m, R]区间中的询问操作有效
- 所以对于每个二分区间[L, R],只需要计算[L, m]中的所有修改操作对(m, R]区间中所有询问操作的贡献即可
- 如果区间长度len≤2000,直接暴力所有操作对所有询问的贡献,复杂度O(len²)
- 如果区间长度len>2000,将所有操作全部加到矩阵中,然后暴力计算出矩阵所有位置的值,最后对于所有询问直接查矩阵对应位置(这里可以优化:不用暴力整个矩阵的值,只需要用组合数计算最后一排的值即可)复杂度O(nk)或O(n)
- 分析下整体复杂度:len>2000的递归次数最坏情况下(n=m=100000)约为15次,所以这部分复杂度为O(15n), len<2000的递归次数约为m次,每次复杂度均摊log²(m)次,所以这部分复杂度O(mlogm),整体复杂度O(15n+mlog²m)
- 搞定!
#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;
}