P2220 [HAOI2012]容易題(快速冪)

Lourisy發表於2020-05-14

Describe

為了使得大家高興,小Q特意出個自認為的簡單題(easy)來滿足大家,這道簡單題是描述如下:
有一個數列A已知對於所有的A[i]都是1~n的自然數,並且知道對於一些A[i]不能取哪些值,我們定義一個數列的積為該數列所有元素的乘積,要求你求出所有可能的數列的積的和 mod 1000000007的值,是不是很簡單呢?呵呵!

Input

第一行三個整數n,m,k分別表示數列元素的取值範圍,數列元素個數,以及已知的限制條數。
接下來k行,每行兩個正整數x,y表示A[x]的值不能是y。

Output

一行一個整數表示所有可能的數列的積的和對1000000007取模後的結果。如果一個合法的數列都沒有,答案輸出0。

Sample Input

3 4 5
1 1
1 1
2 2
2 3
4 3

Sample Output

90 

Explain

A[1]不能取1

A[2]不能取2、3

A[4]不能取3

所以可能的數列有以下12種

數列 積

2 1 1 1 2

2 1 1 2 4

2 1 2 1 4

2 1 2 2 8

2 1 3 1 6

2 1 3 2 12

3 1 1 1 3

3 1 1 2 6

3 1 2 1 6

3 1 2 2 12

3 1 3 1 9

3 1 3 2 18

30%的資料n<=4,m<=10,k<=10

另有20%的資料k=0

70%的資料n<=1000,m<=1000,k<=1000

100%的資料 n<=10^9, m<=10^9, k<=10^5,1<=y<=n,1<=x<=m

Solution

首先我們想一下如果沒有限制那麼最後的結果是多少.對於數列中的每一項,我們都可以取1~n,共有m項,最後總結果其實挺整齊的,$(1+2+3+......+n) * (1+2+3+......n) *...... *(1+2+3+......n) $共乘m次,因為每個數都可以從每乘一次的數(1 ~ n)中選一個算出數列的積,最後相加,化簡 \((1+2+3+4+......+n)^m\) ,也就是\(((1+n)*n/2)^m\) .可以看做用了分步乘法原理,共有m次方,也就是每個數對結果的貢獻.

如果一個數被限制了,那麼這個數所在的某一次方中就不是1 ~ n的加和了,要減去被限制的數,沒有被限制的數還是1 ~ n加和,最後m個數遍歷完,將m個得到的結果乘起來,就是最後結果了.

Attention

資料範圍如此之大,快速冪!!!

注意取模,只要對結果沒影響就取模.

去重邊,樣例中就有

Code

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const ll Mod=1000000007;
map<pair<ll,ll>,ll> ma1;
map<ll,ll> ma2;
ll hj[maxn],n,m,k,js;
ll cf(ll now,ll zs){
	ll jl=now%Mod,ans=1;
	while(zs){
		if(zs&1)ans=(ans*(jl%Mod))%Mod;
		jl=(jl*(jl%Mod))%Mod;
		zs>>=1;
	}
	return ans;
}
int main(){
	scanf("%lld%lld%lld",&n,&m,&k);
	for(ll i=1;i<=k;++i){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		if(!ma2[x]) hj[++js]=x;
		if(ma1[make_pair(x,y)])continue;
		ma1[make_pair(x,y)]=1;
		ma2[x]+=y;
	}
	ll ans=1,cj=(n+1)*n/2;
	for(ll i=1;i<=js;++i)
		ans=(ans*((cj-ma2[hj[i]])%Mod))%Mod;
	printf("%lld\n",(ans%Mod)*(cf(cj,m-js)%Mod)%Mod);
	return 0;
}

hzoi

相關文章