HAOI2012 容易题

题意

给定一个长度为\(m\)的数组,每个位置上只能取\(1-n\)的数,并且有些位置不能取一些数,求可能构成的数列的所有数的积的和,也就是先乘起来再加。

分析

一看这数据\(n\)的范围已经到了\(10^9\),显然枚举这个不可能,所以考虑把他们当一个整体用。

如果没有限制,每个位置上可以取的数如下图,idx表示下标,val表示值。

从m开始往前推,对第m-1行,1可以与m行的val都乘一遍,类似分步乘法,2也是,一直到n,最后再把这些数加起来,类似分类加法,就能得到这两行的积的和,是\(\sum_{i=1}^n×\sum_{i=1}^n\),然后发现这其实是一个等差数列,\(\sum_{i=1}^n=\)\(n(n+1)\over2\),所以\(\sum_{i=1}^n×\sum_{i=1}^n=\)(\(n(n+1)\over2\)\()^2\) ,然后用数学归纳法一步一步往前推,可以得到这\(m\)个位置的和为(\(n(n+1)\over2\)\()^m\)\(n(n+1)\over2\)这个式子最大也不会超过longlong,开始我傻了我乘出来十的八十一次方,可以先把这个求出。

然后考虑有限制的情况,假设是\(m-1\)行不能取\(1\),可以得到下图。

这个不太好看,因为每个位置和每个位置没有绝对的关系,所以可以变换得到下图,就是把有限制条件的放到最后再算。

这样的话没有限制条件的就是(\(n(n+1)\over2\)\()^{m-1}\),有限制条件的呢?发现还可以跟之前推的方法类似,就是每个可能取到的数乘一遍再加起来,即\(n(n+1)\over2\) \(-1\),减去别的数也是一样。

于是假设有\(cnt\)个位置有限制,可以先用快速幂求出不受限制的位置的答案,即(\(n(n+1)\over2\)\()^{m-cnt}\),然后枚举这\(cnt\)个位置,每个位置对答案的贡献是\(n(n+1)\over2\)\(-不能取的数的和\),然后记得取Mod就行了。
还有因为题目没说保证给出的条件不重复,所以要去重。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
const int Mod=1e9+7;
struct Node{
    int idx,w;
    bool operator <(const Node &A)const {
        if(idx==A.idx)return w<A.w;
        return idx<A.idx;
    }
}p[N];
ll sum[N];
ll fpow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=(res*a)%Mod;
        a=a*a%Mod;
        b>>=1;
    }
    return res%Mod;
}
int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++)
        scanf("%d%d",&p[i].idx,&p[i].w);
    sort(p,p+k+1);
    int cnt=0;
    int tot=((n+1ll)*n%Mod)/2;
    for(int i=1;i<=k;i++){
        if(p[i].idx!=p[i-1].idx)
            sum[++cnt]=tot;
        else if(p[i].w==p[i-1].w)
            continue;
        sum[cnt]-=p[i].w;
        sum[cnt]=(sum[cnt]+Mod)%Mod;
    } 
    ll ans=fpow(tot,m-cnt);
    for(int i=1;i<=cnt;i++)
        ans=(ans*sum[i])%Mod;
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-05-13 08:39  An_Fly  阅读(128)  评论(0编辑  收藏  举报