P2220 [HAOI2012]容易题

传送门

首先 $(sum_{i=1}^{n}a_i)(sum_{i=1}^{m}b_i)$ 展开以后包含了所有 $ab$ 两两相乘的情况并且每种组合只出现一次

发现展开后刚好和题目对序列价值的定义一样

考虑进一步的,由乘法分配率可以知道 $prod_{i=1}^{n}(sum_{j=1}^{m}j)$ 展开以后就是所有由 $1$ 到 $m$ 的数组成的数列的价值的和

对于题目中没有限制的位,我们可以直接快速幂求出贡献,考虑剩下有限制的位,

对于某一位,设合法的数的集合为 $S$ 那么这一位贡献就是 $sum_{j in S}j$,就是集合 $S$ 元素的和 ,对每一位分别计算然后乘起来即可

具体维护集合的和可以用总和减去不合法的数得到

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e5+7,mo=1e9+7;
inline int fk(int x) { return x>=mo ? x-mo : x; }
int n,m,K;
struct dat {
    int x,y;
    inline bool operator < (const dat &tmp) const {
        return x!=tmp.x ? x<tmp.x : y<tmp.y;
    }
}d[N];
int sum,Ans=1;
inline int ksm(int x,int y)
{
    int res=1;
    while(y)
    {
        if(y&1) res=1ll*res*x%mo;
        x=1ll*x*x%mo; y>>=1;
    }
    return res;
}
int main()
{
    m=read(),n=read(),K=read();
    sum=(1ll*m*(m+1)/2)%mo;
    for(int i=1;i<=K;i++) d[i].x=read(),d[i].y=read();
    sort(d+1,d+K+1); int now=sum,cnt=0;
    for(int i=1;i<=K;i++)
    {
        if(i!=1&&d[i].x!=d[i-1].x) { cnt++; Ans=1ll*Ans*now%mo; now=sum; }
        if(d[i].x!=d[i-1].x||(d[i].x==d[i-1].x&&d[i].y!=d[i-1].y)) now=fk(now-d[i].y+mo);
    }
    if(now!=sum) { Ans=1ll*Ans*now%mo; cnt++; }
    Ans=1ll*Ans*ksm(sum,n-cnt)%mo;
    printf("%d
",Ans);
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/11485974.html