排列计数的一个经典做法就是按照某种顺序将元素不断往序列中插入,不难发现本题的第一问为了方便知道每个位置前面有多少个大于当前位置,我们可以考虑按高度从大到小地将一座座山不断插入序列。假设当前没有高度相同的山,当前考虑插入第 (i) 座山,({a_i}_{val}) 是当前山的关键字,那么我们能插入的位置就有 (min{i, {a_i}_{val}}) 个,根据乘法原理乘起来即可。但现在有相同高度的山就比较难处理了,不难发现难处理的地方在于不同的插入序列可能对应着一种排列方式,那么我们经典的一个想法就是钦定一种插入顺序使得他能对应生成出所有的排列方式。于是我们可以发现,每次让关键字最小的山先插入是能覆盖到所有情况的,因为关键字小的山贡献出来的新位置后面关键字大的山也能插,但反之就不行了。于是我们按照高度为第一关键字从大到小,按关键字为第二关键字从小到大排序,每个位置能插入的位置有 (min{i, {a_i}_{val} + cnt}) 个,(cnt) 为他之前有多少个高度相同的山,根据乘法原理将这些数简单相乘即可。
再来考虑第二问,可以发现不同高度的山是不会对记重有影响的,因此我们只需要考虑每个高度的方案再根据乘法原理简单相乘即可。接下来我们可以发现实际上高度相同的山都是一样的,那么我们的问题可以转化为有 (n) 个无标号的求每个求能放到 (1 sim min{i, {a_i}_{val}}) 个盒子当中,求方案数。因为球是相同的,因此我们一个方案会记重当且仅当某些球会交换位置,于是为了不让球能交换位置,我们钦定每次放球都必须放在上一次放球的那个盒子和以后的盒子中,这样就不会记重了。于是根据这个方法我们有了一个 (dp),令 (dp_{i, j}) 表示前 (i) 个球放到前 (j) 个盒子的方案,那么有转移 (dp_{i, j} = sumlimits_{k = 1} ^ j dp_{i - 1, k}),发现这是一个前缀和的形式,于是我们压掉第一维,可以得到转移 (dp_{i} = dp_{i - 1} + dp_i),注意初始化 (i = 1) 的情况,因为直接初始化 (i = 0) 会出现 (dp_0 = 0) 到后面会计算非法答案。
#include<bits/stdc++.h>
using namespace std;
#define N 1000 + 5
#define Mod 2011
#define rep(i, l, r) for(int i = l; i <= r; ++i)
struct node{
int h, val;
}a[N];
int n, ans, S[N], fac[N], inv[N], dp[N];
int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int Inc(int a, int b){
return (a += b) >= Mod ? a - Mod : a;
}
int Mul(int a, int b){
return a * b % Mod;
}
int Qpow(int a, int b){
int ans = 1;
while(b){
if(b & 1) ans = Mul(ans, a);
a = Mul(a, a), b >>= 1;
}
return ans;
}
bool cmp(node a, node b){
return a.h == b.h ? a.val < b.val : a.h > b.h;
}
int main(){
n = read(), ans = fac[0] = inv[0] = 1;
rep(i, 1, n) fac[i] = Mul(fac[i - 1], i), inv[i] = Qpow(fac[i], Mod - 2);
rep(i, 1, n) a[i].h = read(), a[i].val = read();
sort(a + 1, a + n + 1, cmp);
rep(i, 1, n){
int j = i, cnt = 0;
while(j <= n && a[j].h == a[i].h) ans = Mul(ans, min(i, a[j].val) + cnt), ++cnt, ++j;
i = j - 1;
}
printf("%d ", ans), ans = 1;
rep(i, 1, n){
int j = i + 1, tmp = 0; memset(dp, 0, sizeof(dp));
rep(k, 1, min(i, a[i].val)) dp[k] = 1;
while(j <= n && a[j].h == a[i].h){
rep(k, 1, min(i, a[j].val)) dp[k] = Inc(dp[k], dp[k - 1]);
++j;
}
rep(k, 1, min(i, a[j - 1].val)) tmp = Inc(tmp, dp[k]);
ans = Mul(ans, tmp);
i = j - 1;
}
printf("%d", ans);
return 0;
}