P3217 [HNOI2011]数矩形

题目

  • 给定若干个点,从这些点里面选取4个点构成一个矩形,使得矩形面积最大
  • 原题链接:点这里

算法思路

思路

  • 对于一个矩形,给定他的两条对角线一定长度相等,且有,中点相等
  • 故我们可以通过枚举两个点,得到对角线,再排序过后,对相同的长度相同中点的对角线处理
  • 知道对角线的向量,即可求出矩形的面积

实现细节

  • 本题为了避免出现浮点数,用LL代替double,我们把所有的中点都乘以2,这样就可以只用LL表示中点,避免了精度带来的误差
  • 以及计算距离的时候,用平方来表示原来的数
  • 该题的关键就是自定义排序,让相同的长度相同中点的对角线聚在一起,方便处理

复杂度分析

  • 原本没有想到最坏的情况,看到洛谷题解的评论才想到的
  • 构造一个圆的情况,但是据说圆上最多只有400个点?(如果是1500个点就可以卡掉, O ( n 3 ) O(n^3) O(n3)
  • 因为他是整点,不可能1500个点全部都在同一个圆上
  • 枚举对角线 O ( n 2 ) O(n^2) O(n2),排序 O ( n 2 log ⁡ n 2 ) O(n^2 \log n^2) O(n2logn2),还有一个三层循环的玄学复杂度……不会分析,反正可以理解为上界特别松

代码实现

//#pragma GCC optimize("O3")
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;

#define debug(x) cerr << #x << ": " << x << '\n'
#define bd cerr << "----------------------" << el
#define el '\n'
#define cl putchar('\n')
#define pb push_back
#define eb emplace_back
#define x first
#define y second
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define loop(i, a, b) for (int i = (a); i < (b); i++)
#define dwn(i, a, b) for (int i = (a); i >= (b); i--)
#define ceil(a, b) (a + (b - 1)) / b
#define ms(a, x) memset(a, x, sizeof(a))
#define inf 0x3f3f3f3f
#define db double

typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<db, db> PDD;
typedef pair<LL, LL> PLL;
typedef vector<int> vci;
typedef map<int, int> mii;
typedef mii::iterator mii_it;

const int N = 1500 + 10, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7;
const double PI = acos(-1), eps = 1e-8;

int T, n, m;

LL operator * (PLL a, PLL b)
{
    return a.x * b.y - a.y * b.x;
}

PLL operator + (PLL a, PLL b)
{
    return {a.x + b.x, a.y + b.y};
}

PLL operator - (PLL a, PLL b)
{
    return {a.x - b.x, a.y - b.y};
}

PLL p[N];
struct Segment
{
    PLL v, midp;
    LL len;
}s[N*N];
int tot;

LL get_dist(int i, int j) //获取距离的平方
{
    return (p[i].x - p[j].x) * (p[i].x - p[j].x) 
    + (p[i].y - p[j].y) * (p[i].y - p[j].y);
}

bool cmp(Segment a, Segment b)
{
    if(a.len == b.len)
        return a.midp < b.midp;//按照中点进行排序
    else
        return a.len > b.len; //长度大的前面(小的也是对的)
}
int main()
{
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    rep(i, 1, n)
    {
        cin >> p[i].x >> p[i].y;
        loop(j, 1, i)//和之前的点连成对角线
            s[ ++ tot] = {p[i] - p[j], p[i] + p[j], get_dist(i, j)};
            //            {v, midp, Len}
    }
    sort(s + 1, s + tot + 1, cmp);
    LL ans = 0;
    for (int i = 1, j = 1; i <= tot; i = j)
    {
        while(s[i].len == s[j].len && s[i].midp == s[j].midp)
            j ++ ; //同一个矩形的对角线长度和中点一定相同
        loop (k, i, j)
            loop (t, k + 1, j)
                ans = max(ans, abs(s[k].v * s[t].v) >> 1); //对角线叉乘/2就是矩形面积
    }
    cout << ans;
}
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值