[2018.07.12 T2] B君的第二题

暂无链接

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 得分一样, m m p

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

i = 1 x j = 1 i k = 1 j p = 1 k a [ 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 p 3 ) ,当我们枚举 p 时,就有下面的式子:

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

推广到 k 次:

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

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

考虑公式如下:

( x + k 1 p k 1 ) = i = 0 k 1 ( x i ) ( k 1 p k 1 i )

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

代入原式:

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 ]

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

i = 0 k 1 ( x i ) [ p = 1 x ( k 1 p k 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();}

猜你喜欢

转载自blog.csdn.net/shadypi/article/details/81028122
今日推荐