浙大城市学院校赛补题

Guaba and Computational Geometry

题意:
坐标轴上给定n个矩形,每个矩形都有自己的权值,求两个不相交矩形的权值之和的最大值。
做法:
两个矩形相交,则x轴投影和y轴投影都会相交。那就变成了在某个轴上的线段相交问题了,线段按照左端点排序之后,对于当前线段,求右端点小于他,且权值最大线段。最大权值用一个变量维护就行。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5, mod = 1e9 + 7;
struct node{
    int x1, x2;
    int val;
    bool operator < (const node &k) const {
        return x1 < k.x1;
    }
}a[N];
struct zuo{
    int a, b, c, d;
    int val;
} b[N];
struct t{
    int b, val;
    bool operator < (const t &k) const{
        return b > k.b;
    }
};
priority_queue<t> q;
int get(int n)
{
    int ret = 0;
    int ma = -1;
    for (int i = 1; i <= n; i++)
    {
        while(!q.empty() && q.top().b < a[i].x1)
        {
            ma = max(ma, q.top().val);
            q.pop();
        }
        if (ma != -1) ret = max(ret, ma + a[i].val);
        q.push(t{a[i].x2, a[i].val});
    }

    while (!q.empty()) q.pop();

    return ret;
}
void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld%lld%lld%lld", &b[i].a, &b[i].b, &b[i].c, &b[i].d);
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &b[i].val);
    }


    for (int i = 1; i <= n; i++)
    {
        a[i].x1 = b[i].a, a[i].x2 = b[i].c;
        a[i].val = b[i].val;
    }
    sort(a + 1, a + 1 + n);
    int ans = 0;
    ans = max(ans, get(n));


    for (int i = 1; i <= n; i++)
    {
        a[i].x1 = b[i].b, a[i].x2 = b[i].d;
        a[i].val = b[i].val;
    }
    sort(a + 1, a + 1 + n);
    ans = max(ans, get(n));


    if (ans != 0) printf("%lld\n", ans);
    else puts("-1");
}
signed main()
{
    int tt = 1;
    cin >> tt;
    while (tt--) solve();
    return 0;
}

Distance

题意:
x轴上给定n条线段,求一个点p,点p到这些线段距离之和最小。
输出n个值,分别为前k条线段的那个最小距离和。1<=k<=n。
法一:(堆+贪心)
假设当前已经确定好了一个点p,有两个堆,一个是p点左边的端点形成一个大根堆,一个是p点右边的端点形成一个小根堆。要加入一条新的线段LR,如果L在p左边,R在p右边,那么很明显加入当前线段后,ans不会改变。
如果R在p左边,那么新的点p就会变为左边的最大值,此时用新的点p更新一下ans。
如果L在p右边也类似。
至于ans为什么这么更新,还是不太会证明。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5, mod = 1e9 + 7;
priority_queue<int> q;
priority_queue<int, vector<int>, greater<int> > p;
void solve()
{
    int n;
    cin >> n;
    int ans = 0;
    q.push(-1e10);
    p.push(1e10);
    for (int i = 1; i <= n; i++)
    {
        int l, r;
        scanf("%lld%lld", &l, &r);
        if (r <= q.top())
        {
            ans += q.top() - r;
            p.push(q.top());
            q.pop();
            q.push(l);
            q.push(r);
        }
        else if (l >= p.top())
        {
            ans += l - p.top();
            q.push(p.top());
            p.pop();
            p.push(l);
            p.push(r);
        }
        else
        {
            q.push(l);
            p.push(r);
        }
        printf("%lld\n", ans);
    }

}
signed main()
{
    int tt = 1;
    // cin >> tt;
    while (tt--) solve();
    return 0;
}

法二:(树状数组+二分)
找到这样一个点,这个点左边的右端点==右边的左端点,那么这个点一定最优。
所以一条线段的左端点和右端点离散化后,分别放入两个树状数组即可。找到这个点之后,直接树状数组上二分即可,因为有可能出现很多区间重叠在一起,所以要两次二分,找左边的右端点<=右边的左端点左边的右端点>=右边的左端点
找到之后,就应该开始计数了,计数要求快速求和,还是要借助树状数组,和上面类似,上面树状数组加的权值是1,这里加的权值变为端点坐标即可。
对于那个绝对值不等式,去绝对值之后变为两部分,都可以用树状数组快速求和。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5, mod = 1e9 + 7;
int c[4][N];
int l[N], r[N];
int b[N];
int cnt;
void add(int k, int i, int val)
{
    while(i <= cnt)
    {
        c[k][i] += val;
        i += i & -i;
    }
}
int que(int k, int i)
{
    int ret = 0;
    while(i)
    {
        ret += c[k][i];
        i -= i & -i;
    }
    return ret;
}
int bsh(int l, int r)
{
    while(l < r)
    {
        int mid = (l + r + 1) >> 1;
        // 找pos,使左边右端点数量等于右边左端点数量
        if (que(1, mid) <= que(0, cnt) - que(0, mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}
int bsh2(int l, int r)
{
    while(l < r)
    {
        int mid = (l + r) >> 1;
        // 找pos,使左边右端点数量等于右边左端点数量
        if (que(1, mid) >= que(0, cnt) - que(0, mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}
void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld%lld", &l[i], &r[i]);
        b[++cnt] = l[i];
        b[++cnt] = r[i];
    }
    
    sort(b + 1, b + 1 + cnt);
    cnt = unique(b + 1, b + 1 + cnt) - b - 1;
    for (int i = 1; i <= n; i++)
    {
        l[i] = lower_bound(b + 1, b + 1 + cnt, l[i]) - b;
        r[i] = lower_bound(b + 1, b + 1 + cnt, r[i]) - b;
    }

    for (int i = 1; i <= n; i++)
    {
        add(0, l[i], 1);
        add(1, r[i], 1);

        add(2, l[i], b[l[i]]);
        add(3, r[i], b[r[i]]);

        int pos = bsh(1, cnt);
        int left = que(1, pos), right = que(0, cnt) - que(0, pos);
        int ans = b[pos] * left - que(3, pos);
        ans += que(2, cnt) - que(2, pos) - b[pos] * right;

        int pos1 = bsh2(1, cnt);
        left = que(1, pos1), right = que(0, cnt) - que(0, pos1);
        int ans1 = b[pos1] * left - que(3, pos1);
        ans1 += que(2, cnt) - que(2, pos1) - b[pos1] * right;

        printf("%lld\n", min(ans, ans1));
    }
}
signed main()
{
    int tt = 1;
    // cin >> tt;
    while (tt--) solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值