暂无链接
B君的第二题
【问题描述】
申生在内而亡,重耳在外而安
考虑
k+1
个数组
a[i](0≤i≤k)
。
为了方便起见,每个数组
a[i]
长度为
n
,下标从
1
开始。(直观来说就是第一维下标从
0
开始,第二维下标从1开始。)
其中
a[i]
时时刻刻是
a[i−1](1≤i≤k)
的前缀和。
前缀和就是
a[i][1]=a[i−1][1]
且
a[i][j]=a[i][j−1]+a[i−1][j](j≥2)
。
比如
a[0]=1,0,0,0
,那么
a[1]=1,1,1,1,a[2]=1,2,3,4,a[3]=1,3,6,10
此时如果我们修改
a[0][3]+=1
,得到新的
a[i]
。
a[0]=1,0,1,0,a[1]=1,1,2,2,a[2]=1,2,4,6,a[3]=1,3,7,13
。
你需要支持
2
个操作。
修改操作:输入
x,y
,执行
a[0][x]+=y
。
询问操作:输入
x
,返回
a[k][x]
的值。
由于结果可能很大,你只需要输出询问的值对
1000000007
取模的结果。
【输入格式】
第一行三个整数
n,m,k
,分别表示数组长度,操作次数,前缀和次数。
接下来
m
行,每行一个操作。
如果第一个数字是
0
,接下来会有
2
个数字
x,y
表示修改,
a[0][x]+=y
。
如果第一个数字是
1
,接下来会有
1
个数字
x
表示询问
a[k][x]
。
【输出格式】
对于每个询问操作,输出询问的值对
1000000007
取模的结果。
【输入样例】
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
【数据范围】
对于
100%
的数据,满足
1≤n≤100000,1≤m≤100000,1≤k≤10
。
对于
100%
的数据,满足
1≤x≤n,0≤y<1000000007
。
对于
30%
的数据,满足
1≤n,m≤1000
。
对于另
40%
的数据,满足
1≤k≤2
。
数据非常有梯度。
题解
感谢毕克不杀之恩。
终于有一道我不止会暴力的题,感觉能拿
70
分,一个小时写完,跟暴力拍了几组大样例,感觉稳了啊,如果不是有个地方没取膜的话。。。
最后只有
50
分,因为少取了一次膜,跪了
20
分,跟暴力分+
k=1
得分一样,
mmp
。
虽然
70
分很好得,但是要
A
掉这道题,还是需要一些骚操作,考虑求前缀和四次的式子:
∑i=1x∑j=1i∑k=1j∑p=1ka[0][p]
每当出现一个合法的
i,j,k,p
的组合,即满足
1≤p≤k≤j≤i≤x
时,
a[0][p]
就会被计算一次。不等式又可以表示为
1<p+1<k+2<j+3<i+4<x+5
,那么一个合法的
i,j,k,p
组合就相当于在
(p+1,x+5)
中选择
3
个数,等于
(x+3−p3)
,当我们枚举
p
时,就有下面的式子:
∑p=1x(x+3−p3)a[0][p]
推广到
k
次:
∑p=1x(x+k−1−pk−1)a[0][p]
目前为止,我们就得到了一个
O(nm)
的做法,还需要一波丧心病狂精妙绝伦的化简。
考虑公式如下:
(x+k−1−pk−1)=∑i=0k−1(xi)(k−1−pk−1−i)
组合意义是讲
x+k−1−p
分为
x,k−1−p
两部分,再枚举从
x
那部分选出
i
个数的方案,与从
k−1−p
个数中选
k−1−i
个数的方案相乘。
代入原式:
∑p=1x(x+k−1−pk−1)a[0][p]=∑p=1x∑i=0k−1(xi)(k−1−pk−1−i)a[0][p]
此时
∑k−1i=0(xi)
已经跟
p
无关了,我们把枚举
p
的部分放到后面去:
∑i=0k−1(xi)[∑p=1x(k−1−pk−1−i)a[0][p]]
后面方括号框住的部分是一个前缀和的形式,而
k−1−i
的取值只有
k
个,所以我们直接上
k
个数据结构维护就好了。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=8e5,mod=1e9+7;
int n,m,k,inv[M],base=1;
ll sum[18][M];
void in(){scanf("%d%d%d",&n,&m,&k);}
void build(){while(base<n)base<<=1;}
void add(int id,int x,int y){x+=base;for(;x;x>>=1)sum[id][x]=(sum[id][x]+y)%mod;}
int ask(int id,int ri)
{
int le=base,ans=0;ri+=base+1;
for(;le^ri^1;le>>=1,ri>>=1) {if(le&1^1)ans=(ans+sum[id][le+1])%mod;if(ri&1)ans=(ans+sum[id][ri-1])%mod;}
return ans;
}
void ac()
{
build();int op,x,y;ll ans,C;
--k;inv[1]=1;for(int i=2;i<=n+n;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
for(int i=1;i<=m;++i)
{
scanf("%d%d",&op,&x);
if(op){ans=0,C=1;for(int j=0;j<=k;++j)ans=(ans+C*ask(k-j,x))%mod,C=C*(x-j)%mod*inv[j+1]%mod;printf("%lld\n",ans);}
else{scanf("%d",&y);C=1;for(int j=0;j<=k;++j){add(j,x,1ll*y*C%mod);C=C*(k-x-j)%mod*inv[j+1]%mod;if(C<0)C+=mod;}}
}
}
int main(){in();ac();}