[2018.07.12 T2] B君的第二题

本文介绍了一种涉及多维数组前缀和计算的问题解决方法,通过巧妙利用组合数学中的组合数公式,将原始复杂度较高的算法优化至高效可行的方案。文章详细解释了如何通过化简公式达到减少计算量的目的。

暂无链接

B君的第二题

【问题描述】

申生在内而亡,重耳在外而安

考虑k+1k+1个数组a[i](0ik)a[i](0≤i≤k)

为了方便起见,每个数组a[i]a[i]长度为nn,下标从1开始。(直观来说就是第一维下标从00开始,第二维下标从1开始。)

其中a[i]时时刻刻是a[i1](1ik)a[i−1](1≤i≤k)的前缀和。

前缀和就是a[i][1]=a[i1][1]a[i][1]=a[i−1][1]a[i][j]=a[i][j1]+a[i1][j](j2)a[i][j]=a[i][j−1]+a[i−1][j](j≥2)

比如a[0]=1,0,0,0a[0]=1,0,0,0,那么a[1]=1,1,1,1,a[2]=1,2,3,4,a[3]=1,3,6,10a[1]=1,1,1,1,a[2]=1,2,3,4,a[3]=1,3,6,10此时如果我们修改a[0][3]+=1a[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,13a[0]=1,0,1,0,a[1]=1,1,2,2,a[2]=1,2,4,6,a[3]=1,3,7,13

你需要支持22个操作。

修改操作:输入x,y,执行a[0][x]+=ya[0][x]+=y

询问操作:输入xx,返回a[k][x]的值。

由于结果可能很大,你只需要输出询问的值对10000000071000000007取模的结果。

【输入格式】

第一行三个整数n,m,kn,m,k,分别表示数组长度,操作次数,前缀和次数。

接下来mm行,每行一个操作。

如果第一个数字是0,接下来会有22个数字x,y表示修改,a[0][x]+=ya[0][x]+=y

如果第一个数字是11,接下来会有1个数字xx表示询问a[k][x]

【输出格式】

对于每个询问操作,输出询问的值对10000000071000000007取模的结果。

【输入样例】

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%的数据,满足1n100000,1m100000,1k101≤n≤100000,1≤m≤100000,1≤k≤10

对于100%100%的数据,满足1xn,0y<10000000071≤x≤n,0≤y<1000000007

对于30%30%的数据,满足1n,m10001≤n,m≤1000

对于另40%40%的数据,满足1k21≤k≤2

数据非常有梯度。

题解

感谢毕克不杀之恩。

终于有一道我不止会暴力的题,感觉能拿7070分,一个小时写完,跟暴力拍了几组大样例,感觉稳了啊,如果不是有个地方没取膜的话。。。

最后只有5050分,因为少取了一次膜,跪了2020分,跟暴力分+k=1k=1得分一样,mmpmmp

虽然7070分很好得,但是要AA掉这道题,还是需要一些骚操作,考虑求前缀和四次的式子:

i=1xj=1ik=1jp=1ka[0][p]

每当出现一个合法的i,j,k,pi,j,k,p的组合,即满足1pkjix1≤p≤k≤j≤i≤x时,a[0][p]a[0][p]就会被计算一次。不等式又可以表示为1<p+1<k+2<j+3<i+4<x+51<p+1<k+2<j+3<i+4<x+5,那么一个合法的i,j,k,pi,j,k,p组合就相当于在(p+1,x+5)(p+1,x+5)中选择33个数,等于(x+3p3),当我们枚举pp时,就有下面的式子:

p=1x(x+3p3)a[0][p]

推广到kk次:

p=1x(x+k1pk1)a[0][p]

目前为止,我们就得到了一个O(nm)O(nm)的做法,还需要一波丧心病狂精妙绝伦的化简。

考虑公式如下:

(x+k1pk1)=i=0k1(xi)(k1pk1i)(x+k−1−pk−1)=∑i=0k−1(xi)(k−1−pk−1−i)

组合意义是讲x+k1px+k−1−p分为x,k1px,k−1−p两部分,再枚举从xx那部分选出i个数的方案,与从k1pk−1−p个数中选k1ik−1−i个数的方案相乘。

代入原式:

p=1x(x+k1pk1)a[0][p]=p=1xi=0k1(xi)(k1pk1i)a[0][p]∑p=1x(x+k−1−pk−1)a[0][p]=∑p=1x∑i=0k−1(xi)(k−1−pk−1−i)a[0][p]

此时k1i=0(xi)∑i=0k−1(xi)已经跟pp无关了,我们把枚举p的部分放到后面去:

i=0k1(xi)[p=1x(k1pk1i)a[0][p]]∑i=0k−1(xi)[∑p=1x(k−1−pk−1−i)a[0][p]]

后面方括号框住的部分是一个前缀和的形式,而k1ik−1−i的取值只有kk个,所以我们直接上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();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值