(\)
(Description)
一个长度为(N)的数列(A),每个位置的数域都为([0,M]igcup N^ ext{*}),定义数列(A)的积为(prod_{i=1}^N A_i)。
现共有(K)个限制条件,以第(x_i)个位置数字不能为(y_i)的形式给出,求所有可能的数列(A)的积求和对(10^9+7)取模的值。
- (Nin [0,10^9]),(Min [0,10^9]),(Kin [0,10^5]),(x_iin[1,N]),(y_iin [1,M])
(\)
(Solution)
- 考虑爆搜的过程,其实是枚举每一位的数,再枚举下一位,假设第(i)位可行的数域为(S_i),答案可以表示成:(egin{align}prod_{i=1}^N sum_{jin S_i} A_iend{align})
- 注意到数列位数非常多,但是限制条件非常少,所以会有很多位置没有限制。注意到答案是一堆求和的连乘积的形式,是满足交换律的,所以直接快速幂求出所有无限制部分连乘积的答案,剩下一个个处理有限制的部分即可,注意限制有可能重复,可以排序去重,总复杂度( ext 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
",res);
return 0;
}