20190916模拟赛T1 求和

$\mathrm{Problem’s\ Website}$

求和(洛谷)

[HAOI2012]容易题(easy)(大视野评测)

$\mathrm{Problem’s \ Description}$

有一个数列,有$m$个元素,每个元素为$1 \thicksim n$,其中有$k$个限制,使第$i$个元素不能取$num$,问所有排列方案的每个元素的乘积和

$\mathrm{Solution}$

一看这道题,感觉毫无思路,于是我们来推一下式子,我们假设$n=2,m=2,k=0$,设$A$为第一个元素可能的值的集合,$B$为第二个元素可能的值,那我们不难得出

$ans=A_1 \times B_1 + A_1 \times B_2 + A_2 \times B_1 + A_2 \times B_2$

$\Rightarrow \ A_1 \times (B_1 + B_2) \times A_2 \times (B_1 + B_2)$

$\Rightarrow \ (A_1 + A_2) \times (B_1 + B_2)$

当我们推出这个式子时,我们可以已经拿到$70$分的好成绩了,但想要$\mathrm{AC}$还要再进行优化,虽然$n$的范围很大,但是$k$的范围并不大,于是我们可以单独处理$k$,因为如果没有任何限制,那么$A_1 + A_2 \ == \ B_1 + B_2$,所以除了有限制的元素,其他的元素是一样的,所以我们可以直接快速幂求解,然后我们记录一下有限制的元素的编号和限制的数值,单独处理即可$\mathrm{AC}$。

总结一下式子:

$ans=((1 + n) n / 2) ^ {m - cnt} \times \prod\limits^{cnt}_{i=1}{((1+n)n/2) - a[i]}$

$cnt$为去重后限制元素的个数,$a$数组为限制的值的和。

最后温馨提示:取模要注意负数 & 要开$\mathrm{long\ long}$

$\mathrm{Code}$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//Coded by Dy.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gc getchar()
#define pc(x) putchar(x)
typedef long long ll;
inline ll sc() {
ll xx = 0, ff = 1; char cch = gc;
while(!isdigit(cch)) {
if(cch == '-') ff = -1; cch = gc;
}
while(isdigit(cch)) {
xx = (xx << 1) + (xx << 3) + (cch ^ '0'); cch = gc;
}
return xx * ff;
}
inline void out(ll x) {
if(x < 0)
pc('-'), x = -x;
if(x >= 10)
out(x / 10);
pc(x % 10 + '0');
}
const int Maxk = 1e5 + 10;
const int mod = 1000000007LL;
#define re register
struct LIM {
ll id, num;
bool operator < (const LIM &x) const {
if(id == x.id)
return num < x.num;
return id < x.id;
}
}lim[Maxk];
ll n, m, k, cnt, ans = 1LL;
ll a[Maxk];
inline ll qw(ll x, ll y) {
ll ans = 1LL, base = (x % mod);
while(y) {
if(y & 1)
ans = (ans * base) % mod;
base = (base * base) % mod;
y >>= 1;
}
return ans;
}
int main() {
// freopen("sum.in", "r", stdin);
// freopen("sum.out", "w", stdout);
n = sc(), m = sc(), k = sc();
for(re int i = 1; i <= k; ++i) {
ll x = sc(), y = sc();
lim[i].id = x, lim[i].num = y;
}
std :: sort(lim + 1, lim + k + 1);
for(re int i = 1; i <= k; ++i) {
if(lim[i].id == lim[i - 1].id) {
if(lim[i].num == lim[i - 1].num)
continue;
a[cnt] += lim[i].num;
}
else {
a[++cnt] += lim[i].num;
}
}
ll tmp = (((1LL + n) * n) >> 1) % mod;
ans = qw(tmp, (m - cnt));
for(re int i = 1; i <= cnt; ++i) {
ans = (ans * (tmp - a[i] + mod) % mod) % mod;
}
out(ans), pc('\n');
return 0;
}

$\mathrm{rp++}$