bzoj刷题记录:4.5-4.9

博主分享了4.5日至4.9日连续五天在bzoj上的刷题记录,涉及题目包括合金、windy数、最大土地面积等。通过解析各个题目的解决方案,展示了如何应用数位DP、后缀数组、旋转卡壳等算法。部分题目如骑士问题和汉诺塔通过dp和递归解决,而一些复杂问题如矿场搭建则需要利用割点集和tarjan算法。
摘要由CSDN通过智能技术生成

bzoj刷题记录:4.5-4.9


bzoj1027: [JSOI2007]合金

神题…

首先第三维可以忽略。

  1. 若干个原材料能合成一种合金,当且仅当其 (x,y) 在这几种原材料的多边形内。因此变成一个问题就是包住目标节点的最小节点数多边形。
  2. 然后居然可以floyd… (i,j) 有边,如果所有目标节点都在 (i,j) 连线的右侧。然后多边形就变成一个环了。最小环就是答案。
#include <bits/stdc++.h>
using namespace std;

struct p {
    double x, y;
    p operator - (const p &a)
    { return (p) {x-a.x, y-a.y}; }
    double operator * (const p &a)
    { return x*a.y-y*a.x; }
} mat[505], tar[505];
int m, n;
double tmp, eps = 1e-10;

double dot(const p &a, const p &b)
{
    return a.x*b.x + a.y*b.y;
}

int sgn(double x)
{
    if (abs(x) <= eps) return 0;
    return x > 0 ? 1 : -1;
}

bool on_side(int i, int j)
{
    for (int k = 1; k <= n; k++)
        if ((tar[k]-mat[i])*(mat[j]-mat[i]) > eps ||
            (abs((tar[k]-mat[i])*(mat[j]-mat[i])) <= eps && dot(mat[i]-tar[k], mat[j]-tar[k]) > eps)) return 0;
    // cout << i << " " << j << " " << gt << " " << ls << " " << endl;
    return 1;
}

int g[505][505];

int main()
{
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; i++)
        scanf("%lf%lf%lf", &mat[i].x, &mat[i].y, &tmp);
    for (int i = 1; i <= n; i++)
        scanf("%lf%lf%lf", &tar[i].x, &tar[i].y, &tmp);
    memset(g, 127/3, sizeof g);
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= m; j++)
            if (on_side(i, j))
                g[i][j] = 1;
    for (int k = 1; k <= m; k++)
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= m; j++)
                g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
    int ans = 233333333;
    for (int i = 1; i <= m; i++) ans = min(ans, g[i][i]);
    if (ans >= 23333333) cout << -1 << endl;
    else cout << ans << endl;
    return 0;
}

bzoj1026: [SCOI2009]windy数

第一道数位dp233…果然神烦,调了一个小时左右吧(主要还是递推关系找错)
就是用 dp[i][j][k] 表示前i位中,最后一位填j,能否随便填/为前导零,然后一堆判断即可。
small trick:用把逻辑判断深入 中,防止逻辑出错。
偷了个懒用了ctrl+c ctrl+v
另外吐槽黄学长代码随手hack…

#include <bits/stdc++.h>
using namespace std;

long long dp[20][10][4]; // 前i位,最后一位是j,能不能随便填/前导0
long long A, B;
int SA[20], SB[20];
int lA = 0, lB = 0;

int main()
{
    scanf("%lld%lld", &A, &B); A--;
    for (long long i = A; i; i /= 10) SA[++lA] = i%10;
    for (long long i = B; i; i /= 10) SB[++lB] = i%10;
    /* calc ans ls B*/
    for (int i = 1; i < SB[lB]; i++) dp[lB][i][1] = 1;
    dp[lB][SB[lB]][0] = 1, dp[lB][0][2] = 1;
    for (int i = lB-1; i >= 1; i--) {
        for (int j = 0; j < 10; j++) {
            for (int k = 0; k <= 2; k++)
                for (int l = 0; l < 10; l++)
                    for (int p = 0; p <= 2; p++)
                        dp[i][j][k] += dp[i+1][l][p]*(p==2||abs(j-l) >= 2)*
                        ((k==0&&p==0&&j==SB[i])||(k==1&&(p==1||(p==2&&j)||(p==0&&j<SB[i])))||(k==2&&j==0&&p==2));
        }
    }
    long long ans1 = 0, ans2 = 0;
    for (int i = 0; i < 10; i++) ans1 += dp[1][i][0]+dp[1][i][1];//+dp[1][i][2];
    memset(dp, 0, sizeof dp);
    /* now ls A*/
    for (int i = 1; i < SA[lA]; i++) dp[lA][i][1] = 1;
    dp[lA][SA[lA]][0] = 1, dp[lA][0][2] = 1;
    for (int i = lA-1; i >= 1; i--) {
        for (int j = 0; j < 10; j++) {
            for (int k = 0; k <= 2; k++)
                for (int l = 0; l < 10; l++)
                    for (int p = 0; p <= 2; p++)
                        dp[i][j][k] += dp[i+1][l][p]*(p==2||abs(j-l) >= 2)*
                        ((k==0&&p==0&&j==SA[i])||(k==1&&(p==1||(p==2&&j)||(p==0&&j<SA[i])))||(k==2&&j==0&&p==2));
        }
    }
    for (int i = 0; i < 10; i++) ans2 += dp[1][i][0]+dp[1][i][1];
    cout << ans1-ans2 << endl;
    return 0;
}

bzoj1069: [SCOI2007]最大土地面积

显然(表示蒟蒻看不出哪里显然)四边形的两个点是凸包上一对对踵点,所以旋转卡壳找对踵点然后枚举另外左右两个点就好了。

#include <bits/stdc++.h>
using namespace std;

struct vec {
    double x, y;
    double len_pow() const
    { return x*x+y*y; }
    friend vec operator - (const vec &a, const vec &b)
    { return (vec) {a.x-b.x, a.y-b.y}; }
    friend double operator * (const vec &a, const vec &b)
    { return a.x*b.y-a.y*b.x; }
    friend bool operator == (const vec &a, const vec &b)
    { return b.x == a.x && b.y == a.y; }
} pts[2005];
int n;

vec lft;
double eps = 1e-7;
int sgn(double a)
{ return abs(a) <= eps ? 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值