P4859 已经没有什么好害怕的了(二项式反演)、P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包、P1452 【模板】旋转卡壳 | [USACO03FAL

20 篇文章 0 订阅

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

代码思路

  1. 读取输入的 n 和 k ,以及 n 个 a 和 b 的值。
  2. 初始化组合数 C :通过两层循环计算组合数,并对结果取模。
  3. 对 a 和 b 数组进行排序。
  4. 计算 F 数组:通过一个嵌套的循环,根据前面的计算结果和条件计算 F[i][j] 的值。
  5. 计算 f 数组:通过从后往前的循环,结合前面的计算结果计算 f[j] 的值。
  6. 计算最终的答案 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;
}

代码思路

  1. 首先,定义了一些常量和数据结构:N 表示点的最大数量。Point 结构体用来表示点的坐标 (x, y) 。p[N] 用于存储输入的点,s[N] 用于构建凸包过程中的临时点存储。n 表示输入点的数量,top 用于记录凸包顶点的数量。

  2. 定义了三个函数:cross 函数用于计算三个点形成的向量的叉积,通过叉积的正负来判断点的相对位置关系。dis 函数用于计算两个点之间的距离。cmp 函数用于比较两个点的先后顺序,先比较 x 坐标,若相同再比较 y 坐标。

  3. Andrew 函数用于计算凸包的周长:首先对输入的点按照 x 坐标优先,y 坐标其次的规则进行排序。然后通过两个循环分别构建下凸包和上凸包。在构建过程中,通过叉积的判断来决定是否将当前点加入凸包顶点数组 s 中,并更新 top 值。最后,计算凸包顶点之间的距离之和,得到凸包的周长。

  4. 在 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 。

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱姌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值