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\n",Ans); return 0; }