[haoi2012]容易题(数论+容斥的思想)
描述 Description
为了使得大家高兴,小Q特意出个自认为的简单题(easy)来满足大家,这道简单题是描述如下:
有一个数列A已知对于所有的A[i]都是1~n的自然数,并且知道对于一些A[i]不能取哪些值,我们定义一个数列的积为该数列所有元素的乘积,要求你求出所有可能的数列的积的和 mod 1000000007的值,是不是很简单呢?呵呵!
输入格式 Input Format
第一行三个整数n,m,k分别表示数列元素的取值范围,数列元素个数,以及已知的限制条数。
接下来k行,每行两个正整数x,y表示A[x]的值不能是y。
输出格式 Output Format
一行一个整数表示所有可能的数列的积的和对1000000007取模后的结果。如果一个合法的数列都没有,答案输出0。
样例输入 Sample Input
3 4 5
1 1
1 1
2 2
2 3
4 3
样例输出 Sample Output
90
时间限制 Time Limitation
1s
注释 Hint
样例解释
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
来源 Source
haoi2012
思路:因为这个数列A[i]的取值范围是1~n,所以显然可以发现这个数位上的数字和为(1+n)*n/2。然后又可以发现这个数列最终的乘积就为各个数位上可选数之和的乘积,自己试一下便可以发现。因为k的范围比较小,所以直接先算出这些有限制的位的乘积,然后在用快速幂算出剩余位置的乘积,最后相乘即可。
注意:记得所有变量全用long long,如果一个数x要取模k,那么你可以这样写(x%k+k)%k。这样就可以防止x为负数的时候取模出错(反正最后改了这两个就A了,不然只能过小数据)
代码如下:
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<cmath> #include<string> #define K 1000000007 #define ll long long using namespace std; inline ll read() { char ch=getchar(); ll x=0,f=1; while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } ll n,m,k; ll M; struct shadow { ll x,y; }a[101000],c[101000],b[106000]; ll d[101000]; bool mycmp(shadow x,shadow y) {return x.x==y.x?x.y<y.y:x.x<y.x;} ll work(ll p) { ll ans=1; ll temp=p,s=M; while(temp!=0) { if(temp%2==1) { ans*=s; ans=(ans%K+K)%K; } ans%=K; temp/=2; s=(s*s%K+K)%K; } return ans; } int main() { memset(c,0,sizeof(c)); n=read();m=read();k=read(); M=(1+n)*n/2%K; for(ll i=1;i<=k;i++) { a[i].x=read(); a[i].y=read(); c[i].x=a[i].x; c[i].y=a[i].y; } sort(a+1,a+1+k,mycmp); sort(c+1,c+1+k,mycmp); ll l=0; for(ll i=1;i<=k;i++) { if(c[i].x==c[i+1].x&&c[i].y==c[i+1].y) continue; b[++l]=c[i]; } /*for(ll i=1;i<=l;i++) cout<<b[i].x<<' '<<b[i].y<<endl;*/ for(ll i=1;i<=l;i++) d[i]=M; ll t=0; for(ll i=1;i<=l;i++) { bool f=false; if(b[i].x==b[i+1].x) { t++; while(b[i].x==b[i+1].x) { d[t]-=b[i].y; d[t]=(d[t]%K+K)%K; i++; f=true; } if(f)//这个就是如果第i位和第i+1位相同,再把第i+1位也减去 { d[t]-=b[i].y; d[t]=(d[t]%K+K)%K; } } else d[++t]-=b[i].y; } ll h=work(m-t); ll ans=1; for(ll i=1;i<=t;i++) ans*=d[i],ans%=K; ans=(ans*h%K+K)%K; cout<<ans<<endl; return 0; }