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