4780. 【GDOI2017模拟9.14】三角形(值域分块 + 经典势能分析 + 奇妙优化)

版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/88976583

Problem

https://jzoj.net/senior/#main/show/4780

给定 n n 个点,求新增一个点后最多能组成多少个平行于坐标轴的三角形。

Solution

实际上可以把新增一个点的代价表示为 S x S y + N u m x + N u m y Sx*Sy+Numx+Numy

那么一个经典套路是,因为 S x , S y Sx,Sy 的取值只有 n \sqrt{n} 种,所以我们暂且把它分个类,并对每一种 S x Sx 相同的按照 N u m x Numx 排好序, S y Sy 亦然。

于是每次枚举一种 S x Sx 的取值,再枚举一种 S y Sy 的取值,然后枚举对应的 N u m x , N u m y Numx,Numy ,这样就具体到了某个点。

如果这个点先前不存在,那么就可以直接退出了(我们把 S y Sy 相同的)否则,我们就继续往下做。根据势能分析可以得知这样做的时间复杂度是 O ( n l o g n ) O(nlogn)

由于我们至少要枚举三层,所以总的时间复杂度是 O ( n n + n l o g n ) O(n\sqrt{n}+nlogn)

注意,如果仅仅这么打,会T飞,但只要加上一个小小的优化,就是每次找到一个先前不存在的点后记录一下对应的 N u m y Numy 值在 k k 这个位置,那么以后枚举的 N u m y Numy 由于 N u m x Numx 减小的缘故,都仅仅只会到 k 1 k-1 这个地方,于是就可以做到上面那个优秀的时间复杂度了(想一想,凭什么)。

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);
}

猜你喜欢

转载自blog.csdn.net/Algor_pro_king_John/article/details/88976583