题号 NC19989
名称 [HAOI2012]容易题(EASY)
来源 [HAOI2012]

题目描述

为了使得大家高兴,小Q特意出个自认为的简单题(easy)来满足大家,这道简单题是描述如下:

有一个数列A已知对于所有的A[i]都是1~n的自然数,并且知道对于一些A[i]不能取哪些值,我们定义一个数列的积为该数列所有元素的乘积,要求你求出所有可能的数列的积的和 mod 1000000007的值,是不是很简单呢?呵呵!

样例

输入
3 4 5
1 1
1 1
2 2
2 3
4 3
输出
90
样例解释
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

算法

(数学 + 快速幂)

假如没有限制,题意类似于给你m个1 ~ n的数的集合,(1,2,...,n)、(1,2,...,n)、...分别从这些集合中选一个数相乘,求所有这些乘积之和,如果我们将这些集合(1,2,...,n)、(1,2,...,n)、...,写成(1 + 2 + ... + n) * (1 + 2 + ... + n) * ... 的形式,根据我们的运算习惯我们会将这些括号展开一个一个求解,即先从第一个括号中拿1,然后从第二个括号中再拿1,...,从最后一个括号中拿1,相乘(1 * 1 * ...1)得1,然后再从从第一个括号中拿1,然后从第二个括号中再拿1,...,从最后一个括号中拿2,相乘(1 * 1 * ...2)得2,重复这样的操作,我们可以发现这种操作正好和题目要求的运算是一致的,也就是说(1 + 2 + ... + n) * (1 + 2 + ... + n) * ... 的结果就是答案,(1 + 2 + ... + n) 我们可以O(1)求出来,有m项相乘我们可以用快速幂优化。加上限制条件,我们就将(1 + 2 + ... + n)中去掉一些数再相乘,只有k个限制需要特殊处理时间复杂度是可以的,要注意的细节是可能有多个相同的限制所以我们需要去重,可以用set来维护。

时间复杂度

C++ 代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
//#include <unordered_map>
#include <vector>
#include <queue>
#include <set>
#include <bitset>
#include <cmath>

#define P 131

#define lc u << 1
#define rc u << 1 | 1

using namespace std;
typedef long long LL;
typedef pair<int,LL> PII;
set<PII> S;
const int N = 100010,mod = 1000000007;
int n,m,k;

LL qmi(LL a,int k)
{
    LL res = 1;
    while(k)
    {
        if(k & 1) res = res * a % mod;
        a = a * a % mod;
        k >>= 1;
    }
    return res;
}

void solve()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 0;i < k;i ++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        S.insert(make_pair(x,y));
    }
    LL sum = (n + 1ll) * n / 2 % mod;
    LL res = 1ll;
    PII tmp = make_pair(-1,(sum - 1) % mod);
    int cnt = 0;
    for(auto &t : S)
    {
        if(t.first != tmp.first)
        {
            res = (res * (sum - tmp.second)) % mod;
            tmp = t;
            cnt ++;
        }else tmp.second = (tmp.second + t.second) % mod;
    }
    res = (res * (sum - tmp.second)) % mod;
    res = (res * qmi(sum,m - cnt)) % mod;
    // cout << S.size() << endl;
    printf("%lld\n",(res % mod + mod) % mod);
}

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    #else
    #endif // LOCAL
    int T = 1;
    // init(500);
    // scanf("%d",&T);
    while(T --)
    {
        // scanf("%lld%lld",&n,&m);
        solve();
        // test();
    }
    return 0;
}