P4859 已经没有什么好害怕的了(二项式反演)
题目描述
P4859 已经没有什么好害怕的了 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2005, P = 1e9 + 9;
int n, k, a[N], b[N];
int F[N][N], f[N], C[N][N];
int main() {
scanf_s("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf_s("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf_s("%d", &b[i]);
// 初始化组合数 C
for (int i = 0; i <= n; i++) C[i][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
// 计算 F 数组
for (int i = 0; i <= n; i++) F[i][0] = 1;
for (int i = 1, p = 0; i <= n; i++) {
while (p + 1 <= n && b[p + 1] < a[i]) p++;
for (int j = 1; j <= n; j++)
F[i][j] = (F[i - 1][j] + 1ll * F[i - 1][j - 1] * (p - j + 1) % P) % P;
}
// 计算 f 数组
for (int j = n, s = 1; j >= 1; j--)
f[j] = 1ll * F[n][j] * s % P, s = 1ll * s * (n - j + 1) % P;
int ans = 0, x = (n + k) / 2;
for (int i = x; i <= n; i++) {
int tmp = 1ll * C[i][x] * f[i] % P;
if ((i - x) & 1) (ans += -tmp + P) %= P;
else (ans += tmp) %= P;
}
printf("%d\n", ans);
}
代码思路
- 读取输入的
n
和k
,以及n
个a
和b
的值。 - 初始化组合数
C
:通过两层循环计算组合数,并对结果取模。 - 对
a
和b
数组进行排序。 - 计算
F
数组:通过一个嵌套的循环,根据前面的计算结果和条件计算F[i][j]
的值。 - 计算
f
数组:通过从后往前的循环,结合前面的计算结果计算f[j]
的值。 - 计算最终的答案
ans
:根据给定的条件和计算出的中间结果进行计算。通过一个循环,根据组合数、f
值和奇偶性来更新答案,并对结果取模。
P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包
题目描述
P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 100010;
struct Point {
double x, y;
} p[N], s[N];
int n, top;
double cross(Point a, Point b, Point c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
double dis(Point a, Point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
bool cmp(Point a, Point b) {
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
double Andrew() {
sort(p + 1, p + n + 1, cmp);
for (int i = 1; i <= n; i++) {
while (top > 1 && cross(s[top - 1], s[top], p[i]) <= 0) top--;
s[++top] = p[i];
}
int t = top;
for (int i = n - 1; i >= 1; i--) {
while (top > t && cross(s[top - 1], s[top], p[i]) <= 0) top--;
s[++top] = p[i];
}
double res = 0;
for (int i = 1; i < top; i++) res += dis(s[i], s[i + 1]);
return res;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
scanf_s("%lf%lf", &p[i].x, &p[i].y);
printf("%.2lf\n", Andrew());
return 0;
}
代码思路
-
首先,定义了一些常量和数据结构:
N
表示点的最大数量。Point
结构体用来表示点的坐标(x, y)
。p[N]
用于存储输入的点,s[N]
用于构建凸包过程中的临时点存储。n
表示输入点的数量,top
用于记录凸包顶点的数量。 -
定义了三个函数:
cross
函数用于计算三个点形成的向量的叉积,通过叉积的正负来判断点的相对位置关系。dis
函数用于计算两个点之间的距离。cmp
函数用于比较两个点的先后顺序,先比较x
坐标,若相同再比较y
坐标。 -
Andrew
函数用于计算凸包的周长:首先对输入的点按照x
坐标优先,y
坐标其次的规则进行排序。然后通过两个循环分别构建下凸包和上凸包。在构建过程中,通过叉积的判断来决定是否将当前点加入凸包顶点数组s
中,并更新top
值。最后,计算凸包顶点之间的距离之和,得到凸包的周长。 -
在
main
函数中:读入点的数量n
。读入每个点的坐标。调用Andrew
函数计算并输出凸包的周长,保留两位小数。
P1452 【模板】旋转卡壳 | [USACO03FALL] Beauty Contest G
题目描述
P1452 【模板】旋转卡壳 | [USACO03FALL] Beauty Contest G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 50010
#define x first
#define y second
#define Point pair<int,int>
Point p[N], s[N];
int n;
int cross(Point a, Point b, Point c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
int dis(Point a, Point b) {
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
void Andrew() {
sort(p + 1, p + n + 1);
int t = 0;
for (int i = 1; i <= n; i++) {
while (t > 1 && cross(s[t - 1], s[t], p[i]) <= 0) t--;
s[++t] = p[i];
}
int k = t;
for (int i = n - 1; i >= 1; i--) {
while (t > k && cross(s[t - 1], s[t], p[i]) <= 0) t--;
s[++t] = p[i];
}
n = t - 1;
}
int rotating_calipers() {
int res = 0;
for (int i = 1, j = 2; i <= n; i++) {
while (cross(s[i], s[i + 1], s[j]) < cross(s[i], s[i + 1], s[j + 1])) j = j % n + 1;
res = max(res, max(dis(s[i], s[j]), dis(s[i + 1], s[j])));
}
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d%d", &p[i].x, &p[i].y);
Andrew();
printf("%d\n", rotating_calipers());
return 0;
}
代码思路
- 首先定义了一些必要的常量、数据结构和函数。
- 在
main
函数中读取输入数据,调用Andrew
函数构建凸包,然后调用rotating_calipers
函数计算凸包上的最大距离并输出。
函数 cross
:用于计算三个点形成的向量的叉积。通过叉积的正负可以判断三个点的相对位置关系。
函数 dis
:计算两个点之间的距离的平方。
函数 Andrew
:先对输入的点进行排序。通过两个循环分别构建下凸包和上凸包。在循环中,根据叉积的大小来决定是否将当前点加入凸包。
函数 rotating_calipers
:这是旋转卡壳算法的实现。通过指针 i
和 j
的移动,计算不同位置的点对之间的距离。当叉积满足一定条件时更新 j
的位置,然后更新最大距离 res
。