2021牛客多校6(CFH)

目录

C:Delete Edges(构造,思维)

F:Hamburger Steak(思维)

 H:Hopping Rabbit(线段树维护扫描线)

C:Delete Edges(构造,思维)

题意:

你有一张n(3≤n≤2000)个点的无向完全图,你需要删掉一些边,让这张图里面边数小于n,输出你删除边的方案。

思路:

这个图一共有\frac{n(n-1)}{2}  条边,我们必须要删除至少\frac{n(n-1)}{2}-n条边。我们需要构造出尽可能多的三元组,还要保证任意两对三元组里相同的点不能超过1个.

官方给出的答案是x+y+z\equiv 0(mod n)的所有解。

那么我们只需要保证x<y<z进行暴力即可。

(证明不会)

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int x, y, z;
};
int main()
{
    int n;
    cin >> n;
    vector<node> v;
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            int k = (n - i - j + n) % n;
            if (k == 0)
                k = n;
            if (j < k)
                v.push_back({i, j, k});
        }
    }
    cout << v.size() << endl;
    for (auto i : v)
    {
        printf("%d %d %d\n",i.x,i.y,i.z);
    }
}

F:Hamburger Steak(思维)

题意:

n块牛排,m个烤盘,每块牛排要烤t_{i}​分钟,而且最多可以分两次烤,只要时间达到t_{i}​就行,输出最小花费时间时,每块牛排的烤制方案,按照时间顺序输出。

 思路:

考虑一个时间上限limt

首先考虑每一块牛排,可以发现limt至少为t_{max}(牛排需要最长时间的那个)

从总体来看,我们还要保证\sum t_{i}<=m*limt成立

也就是limit>=\left \lceil \frac{\sum t_{i}}{m}\right \rceil

limit在这两种情况取一个max,就确定了这个时间上限。

接下来模拟每个锅的分配时间即可。

(注意一个坑点,要按照时间顺序输出)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int t[N];
signed main()
{
    int n, m;
    cin >> n >> m;
    int sum = 0;
    int mx = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> t[i];
        sum += t[i];
        mx = max(mx, t[i]);
    }
    int limt = (sum + m - 1) / m;
    limt = max(mx, limt);
    int id = 1, last = 0;
    for (int i = 1; i <= n; i++)
    {
        if (last + t[i] <= limt)
        {
            cout << 1 << " " << id << " " << last << " " << last + t[i] << endl;
            last += t[i];
        }
        else
        {
            t[i] -= (limt - last);
            cout << 2;
            cout << " " << id + 1 << " " << 0 << " " << t[i] << " ";
            cout << " " << id << " " << last << " " << limt <<endl;
            id++;
            last = 0;
            last += t[i];
        }
        if (last == limt)
            id++, last = 0;
    }
}

 H:Hopping Rabbit(线段树维护扫描线)

题意:

二维平面,一些矩阵块不能进,兔子每次会上下左右选择一个方向,移动dd个单位。找一个起点(x_{0}+0.5,y_{0}+0.5),使得不管怎么走都不会走到非法矩阵块里。

思路:

考虑兔子不动,矩阵往兔子靠近。我们可以把这些矩阵全部缩进一个矩阵(0,0)(d-1,d-1)中表示所有情况,如果这个矩阵有没覆盖的格子,那么那个坐标就是一个合法的答案。

如何缩进一个矩阵(0,0)(d-1,d-1)

来自题解

 在这里插入图片描述

 比如这个我就可以缩成这样

在这里插入图片描述

把每个矩阵对d取模分情况讨论

变成一个(d-1)*(d-1)的矩阵后,我们要找到没有覆盖的点,用扫描线从下往上扫,如果扫到这一行有没有覆盖的点,我们暴力从左往右找就行。

不是很会扫描线,参考别人的代码,唉 

#include <bits/stdc++.h>
// #define int long long
using namespace std;
const int N = 5e5 + 10;
struct scanline
{
    int l, r, x;
};
vector<scanline> v[N];
struct node
{
    int cnt, len; //覆盖多少次,cnt>0的区间总长度
} tre[N];
int n, d;
int ql,qr;
void add(int x1,int x2,int y1,int y2)
{
    v[x1].push_back({y1,y2,1});
    v[x2+1].push_back({y1,y2,-1});
}
void pushup(int rt,int l,int r)
{
    if(tre[rt].cnt)
    tre[rt].len=r-l+1;
    else if(l==r)
    tre[rt].len=0;
    else
    tre[rt].len=tre[rt<<1].len+tre[rt<<1|1].len;
}
void change(int rt,int l,int r,int x)
{
    if(ql<=l&&r<=qr)
    {
        tre[rt].cnt+=x;
        pushup(rt,l,r);
        return ;
    }
    int mid=(l+r)>>1;
    if(ql<=mid)
    change(rt<<1,l,mid,x);
    if(qr>mid)
    change(rt<<1|1,mid+1,r,x);
    pushup(rt,l,r);
}
int get(int rt,int l,int r)
{
    if(tre[rt].len==0)
    {
        return l;
    }
    int mid=(l+r)>>1;
    if(tre[rt<<1].len<mid-l+1)
    return get(rt<<1,l,mid);
    else
    return get(rt<<1|1,mid+1,r);
}
signed main()
{
    scanf("%d%d", &n, &d);
    for (int i = 1; i <= n; i++)
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        --x2, --y2;
        if (x2 - x1 + 1 >= d)
            x1 = 0, x2 = d - 1;
        if (y2 - y1 + 1 >= d)
            y1 = 0, y2 = d - 1;
        x1=(x1%d+d)%d;
        x2=(x2%d+d)%d;
        y1=(y1%d+d)%d;
        y2=(y2%d+d)%d;
        if(x1<=x2)
        {
            if(y1<=y2)
            add(x1,x2,y1,y2);
            else
            add(x1,x2,0,y2),add(x1,x2,y1,d-1);
        }
        else
        {
            if(y1<=y2)
            add(0,x2,y1,y2),add(x1,d-1,y1,y2);
            else
            add(0,x2,0,y2),add(x1,d-1,0,y2),add(0,x2,y1,d-1),add(x1,d-1,y1,d-1);
        }
    }
    for(int i=0;i<d;i++)
    {
        for(int j=0;j<v[i].size();j++)
        {
            ql=v[i][j].l;
            qr=v[i][j].r;//当前扫描线的左右端点
            change(1,0,d-1,v[i][j].x);
        }
        if(tre[1].len<d)
        {
            printf("YES\n");
            printf("%d %d",i,get(1,0,d-1));
            return 0;
        }
    }
    cout<<"NO";
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值