$[\ 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;
}

猜你喜欢

转载自www.cnblogs.com/SGCollin/p/9651195.html
今日推荐