2751: [HAOI2012]容易题(easy)
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2208 Solved: 931
[ Submit][ Status][ Discuss]
Description
为了使得大家高兴,小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
1 1
1 1
2 2
2 3
4 3
Sample Output
样例解释
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
HINT
数据范围
30%的数据n<=4,m<=10,k<=10
另有20%的数据k=0
70%的数据n<=1000,m<=1000,k<=1000
100%的数据 n<=109,m<=109,k<=105,1<=y<=n,1<=x<=m
Source
题解
虽然这个题目就叫容易题而且所有题解都说这个很水
但对于我这个刚学的蒟蒻 还是好难啊啊啊啊
于是乎又去看题解
不过 好歹看懂了 首先 没有限制的话 答案是sum^m(sum=1到n的和)
那么 引用一下http://blog.youkuaiyun.com/jr_mz/article/details/48661767
题意:有一个数列共有m项,其中每一项的值都是不大于n的正整数,并且给出k个形如第i项不能为j的约束。求出所有可能的数列的各项之积之和对1,000,000,007取模的结果。
例如n=2,m=2,约束第1项不能填1,则数列可以是2,1或2,2,每个数列的各项之积依次为2和4,则答案为2+4=6。n,m<=1,000,000,000,k<=1
这里不可能把所有情况都枚举一次,毕竟可能的数列的数量还是指数级别的。
应该注意到,答案也等于各项可取的数之和之积,如在上面的例子中,第1项可取2和为2,第2项可取1或2和为3,则答案为2*3=6。
由于k远小于n,因此只要统计出某些项不能取哪些数,用总和相减就能得到能取的数之和。统计任务可以用快排和哈希完成,还要注意给约束判重。
又因为k也远小于m,实际上绝大部分项是没有约束的,所以即能取的数之和都是总和,由于m很大,因此可以用快速幂来解决。
上面的话中红字就是这题的关键了 算法就是先把有约束的乘起来 然后在把没有约束的乘起来
对于没有约束的 如果有tot个 那么这个乘起来就是sum^tot 这个是要用到快速幂来计算的
还有 排序的目的是为了去重
//参考:http://blog.youkuaiyun.com/jr_mz/article/details/48661767
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define mod 1000000007
using namespace std;
int m,n,k;
ll ans;
struct node{
int pos,val;
}a[100005];
bool cmp(node a,node b){
if(a.pos==b.pos) return a.val<b.val;
return a.pos<b.pos;
}
ll quick_pow(ll a,ll b,ll c){
ll ans=1;
for(ll i=b;i;i>>=1,a=(a*a)%c)
if(i&1) ans=(ans*a)%c;
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++){
scanf("%d%d",&a[i].pos,&a[i].val);
}
sort(a+1,a+1+k,cmp);
int tot=m;ans=1;
ll sum=(ll)n*(n+1)/2%mod;ll tmp=sum;//积的和 = 每一位分开 算出能取到的值的和 最后乘起来
for(int i=1;i<=k;i++){
if(a[i].pos!=a[i-1].pos&&i!=1){
ans=ans*tmp%mod;
tmp=sum;
tot--;
}
if(a[i].pos!=a[i-1].pos||a[i].val!=a[i-1].val){
tmp-=a[i].val;
if(tmp<0) tmp+=mod;
}
}
tot--;ans=ans*tmp%mod;//最后一个还没算
printf("%lld\n",ans*quick_pow(sum,tot,mod)%mod);//没限制的有tot个 每个的和是sum
return 0;
}