[ HAOI 2012 ] 容易题
\(\\\)
\(Description\)
一个长度为\(N\)的数列\(A\),每个位置的数域都为\([0,M]\bigcup N^\text{*}\),定义数列\(A\)的积为\(\prod_{i=1}^N A_i\)。
现共有\(K\)个限制条件,以第\(x_i\)个位置数字不能为\(y_i\)的形式给出,求所有可能的数列\(A\)的积求和对\(10^9+7\)取模的值。
- \(N\in [0,10^9]\),\(M\in [0,10^9]\),\(K\in [0,10^5]\),\(x_i\in[1,N]\),\(y_i\in [1,M]\)
\(\\\)
\(Solution\)
- 考虑爆搜的过程,其实是枚举每一位的数,再枚举下一位,假设第\(i\)位可行的数域为\(S_i\),答案可以表示成:\(\begin{align}\prod_{i=1}^N \ \sum_{j\in S_i}\ A_i\end{align}\)
- 注意到数列位数非常多,但是限制条件非常少,所以会有很多位置没有限制。注意到答案是一堆求和的连乘积的形式,是满足交换律的,所以直接快速幂求出所有无限制部分连乘积的答案,剩下一个个处理有限制的部分即可,注意限制有可能重复,可以排序去重,总复杂度\(\text O(KlogK+logN)\)
\(\\\)
\(Code\)
#include<map>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100010
#define R register
#define gc getchar
#define mod 1000000007
using namespace std;
typedef long long ll;
inline ll rd(){
ll x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
ll res,n,m,k,ptr;
struct query{ll p,x;}q[N];
inline bool cmp(query x,query y){
return (x.p==y.p)?(x.x<y.x):(x.p<y.p);
}
inline ll qpow(ll x,ll t){
ll ans=1;
while(t){
if(t&1) (ans*=x)%=mod;
(x*=x)%=mod; t>>=1;
}
return ans;
}
int main(){
n=rd(); m=rd(); k=rd();
for(R ll i=1;i<=k;++i){q[i].p=rd();q[i].x=rd();}
sort(q+1,q+1+k,cmp);
for(R ll i=1,tmp;i<=k;++i){
tmp=q[i].x;
while(q[i+1].p==q[i].p){
if(q[i+1].x!=q[i].x) tmp+=q[i+1].x; ++i;
}
q[++ptr].p=q[i].p; q[ptr].x=tmp;
}
n=(n*(n+1)/2)%mod;
res=qpow(n,m-ptr);
for(R ll i=1;i<=ptr;++i) res=(res*((n-q[i].x)%mod+mod))%mod;
printf("%lld\n",res);
return 0;
}