版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/88976583
Problem
https://jzoj.net/senior/#main/show/4780
给定 个点,求新增一个点后最多能组成多少个平行于坐标轴的三角形。
Solution
实际上可以把新增一个点的代价表示为
那么一个经典套路是,因为 的取值只有 种,所以我们暂且把它分个类,并对每一种 相同的按照 排好序, 亦然。
于是每次枚举一种 的取值,再枚举一种 的取值,然后枚举对应的 ,这样就具体到了某个点。
如果这个点先前不存在,那么就可以直接退出了(我们把 相同的)否则,我们就继续往下做。根据势能分析可以得知这样做的时间复杂度是 。
由于我们至少要枚举三层,所以总的时间复杂度是 。
注意,如果仅仅这么打,会T飞,但只要加上一个小小的优化,就是每次找到一个先前不存在的点后记录一下对应的 值在 这个位置,那么以后枚举的 由于 减小的缘故,都仅仅只会到 这个地方,于是就可以做到上面那个优秀的时间复杂度了(想一想,凭什么)。
Code
#include <bits/stdc++.h>
#define F(i, a, b) for (LL i = a; i <= b; i ++)
typedef long long LL;
const LL N = 2e3 + 10, K = 11;
using namespace std;
LL n, m, k, l, ans;
LL f[N][K], C[N][N], jc[N], KSM[K][2 * N * N];
int main() {
scanf("%d%d%d%d", &n, &m, &k, &l);
jc[0] = 1;
F(i, 0, max(n, m) + 1)
C[i][0] = 1;
F(i, 1, max(n, m) + 1)
F(j, 1, max(n, m) + 1)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % l;
F(i, 1, max(n, m))
jc[i] = 1ll * jc[i - 1] * i % l;
F(j, 1, k) {
KSM[j][0] = 1;
F(i, 1, 2 * n * m)
KSM[j][i] = KSM[j][i - 1] * j % l;
}
f[0][1] = 1; int w = min(n, m);
F(i, 0, w)
F(j, 1, k - 1)
if (f[i][j])
F(t, 0, w - i)
if (n - i >= t && m - i >= t)
f[i + t][j + 1] = (f[i + t][j + 1] + 1ll * f[i][j] * C[n - i][t] % l * C[m - i][t] % l * jc[t] % l * KSM[j][(n + m - 2 * i - t - 1) * t] % l) % l;
F(i, 1, w)
ans = (ans + ((i & 1) ? (1) : (- 1)) * f[i][k] * KSM[k][(n - i) * (m - i)] % l) % l;
printf("%d\n", (ans + l) % l);
}