[2018.07.12 T2] B君的第二题

暂无链接

B君的第二题

【问题描述】

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

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

为了方便起见,每个数组 a[i] a [ i ] 长度为 n n ,下标从1开始。(直观来说就是第一维下标从 0 0 开始,第二维下标从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,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 个操作。

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

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

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

【输入格式】

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

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

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

如果第一个数字是 1 1 ,接下来会有1个数字 x x 表示询问a[k][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 % 的数据,满足 1n100000,1m100000,1k10 1 ≤ n ≤ 100000 , 1 ≤ m ≤ 100000 , 1 ≤ k ≤ 10

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

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

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

数据非常有梯度。

题解

感谢毕克不杀之恩。

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

最后只有 50 50 分,因为少取了一次膜,跪了 20 20 分,跟暴力分+ k=1 k = 1 得分一样, mmp m m p

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

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

每当出现一个合法的 i,j,k,p i , j , k , p 的组合,即满足 1pkjix 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 个数,等于(x+3p3),当我们枚举 p p 时,就有下面的式子:

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

推广到 k k 次:

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

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

考虑公式如下:

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

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

代入原式:

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ShadyPi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值