2020牛客暑期多校训练营(第八场)

今天爆0了QAQ!!,菜是原罪。
2020牛客暑期多校训练营(第八场)_第1张图片


I、Interesting Computer Game

2020牛客暑期多校训练营(第八场)_第2张图片
2020牛客暑期多校训练营(第八场)_第3张图片

样例1:
输入
2
6
1 2
2 3
3 4
1 4
1 3
2 4
5
1 2
1 2
1 3
2 3
5 6
输出
Case #1: 4
Case #2: 4

题目大意:

给你两个长度为 n n n的数组 A A A B B B,第 i i i步可以从 a i ai ai b i bi bi中选择一个数,求最后选出的数中,不同的数要最多。

题解:

  • 离散化+图论+判断是否有环。
  • 我们将被一步的 a i ai ai b i bi bi当做一条边,如果当前图中的存在环的话,这个图的每一个点都可以选择。如果不存在环的话当前图中有一个点不能选择。
  • 则答案就是:点数的总种类数 - - (k个点,k-1条边的连通分量)。
  • 我们来例举一个case:1 2、2 1、2 3、3 4和1 2、2 3 、3 4。自己动手画一下就知道了。

ACcode:

/*
 * @Author: NEFU_马家沟老三
 * @LastEditTime: 2020-08-04 11:09:22
 * @CSDN blog: https://blog.csdn.net/acm_durante
 * @E-mail: [email protected]
 * @ProbTitle: 
 */
#include 
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define mem(a, b) memset(a, b, sizeof(a))

const int N = 2e5+5;
int f[N],a[N],b[N];
vector<int>q;
bool vis[N];
int find_set(int x){
    return (x == f[x]) ? x : ( f[x] = find_set(f[x]) );
}

void solve(int n){
    mem(vis,0);
    sort(q.begin(),q.end());
    q.erase(unique(q.begin(),q.end()),q.end());
    int len = q.size();
    rep(i,1,len+5) f[i] = i;
    rep(i,1,n){
        int x = lower_bound(q.begin(),q.end(),a[i]) - q.begin() + 1;//离散化处理
        int y = lower_bound(q.begin(),q.end(),b[i]) - q.begin() + 1;
        int xx = find_set(x),yy = find_set(y);
        if(xx == yy){
            vis[xx] = 1;//当前联通分支存在环的话则标记
        }
        else{
            f[xx] = yy;
            if(vis[xx])
                vis[yy] = 1; //f[yy] = yy;xx图中存在环,我们则需要将祖先也标记上存在环
        }
    }
    int ans = 0;
    rep(i,1,len){
        if(vis[i] == 0 && f[i] == i)//判断是否有环
            ++ans;
    }
    printf("%d\n",len - ans);
}
int main()
{
    int t,cnt = 0;
    scanf("%d",&t);
    while(t--){
        q.clear();
        int n;
        scanf("%d",&n);
        rep(i,1,n){
            scanf("%d%d",&a[i],&b[i]);                 
            q.push_back(a[i]);
            q.push_back(b[i]);
        }
        printf("Case #%d: ", ++cnt);
        solve(n);
    }
    return 0;
}

K:Kabaleo Lite

2020牛客暑期多校训练营(第八场)_第4张图片
2020牛客暑期多校训练营(第八场)_第5张图片

样例:
输入
2
3
2 -1 3
3 2 1
4
3 -2 3 -1
4 2 1 2
输出
Case #1: 3 8
Case #2: 4 13

说明
For test case 1, the maximum number of visitors is 3, one of a possible solution is:
The first visitor received food 1, the profit is 2.
The second visitor received food 1, the profit is 2.
The third visitor received food 1 + food 2 + food 3, the profit is 2 + (-1) + 3.

题意:

  • n n n种菜,每种菜的盈利为 a i ai ai,数量为 b i bi bi
  • 每个顾客必须从第 1 1 1种菜开始连续的吃,每种吃 1 1 1个。
  • 回答:在顾客数量最多的前提下,利润最大是多少。
  • 输出顾客数量和利润。

题解:

  • b 1 b1 b1就是最大顾客的数量
  • 我们对 a i ai ai取前缀和, b i bi bi取前缀最小值。对 a i ai ai放入优先队列中(降序),从大到小取就可以。注意判断数量。
  • 坑点:本博主就是在这里被坑的,答案会超 l o n g l o n g long long longlong,牛客支持int128,可以开一下。

ACcode:

/*
 * @Author: NEFU_马家沟老三
 * @LastEditTime: 2020-08-04 11:24:49
 * @CSDN blog: https://blog.csdn.net/acm_durante
 * @E-mail: [email protected]
 * @ProbTitle: 高精度 + 贪心
 */
#include 
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define mem(a, b) memset(a, b, sizeof(a))
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int N = 1e5 + 5;
int n;
struct node
{
    ll num;
    int id;
    bool operator<(const node &A) const{
        if (num != A.num)
            return num < A.num;
        return id < A.id;
    }
} a[N];
 
priority_queue<node> p;
 
int tree[N];
void print(__int128 x)
{
    if(x < 0)
    {
        x = -x;
        putchar('-');
    }
     if(x > 9) print(x/10);
    putchar(x%10 + '0');
}

int main()
{
    //ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t;
    scanf("%d", &t);
    int cnt = 0;
    while (t--)
    {
        while (!p.empty())
            p.pop();
        scanf("%d", &n);
        __int128 ans = 0;
        rep(i, 1, n)
        {
            scanf("%lld", &a[i].num);
            a[i].num += a[i - 1].num;//求利润前缀最大值
            a[i].id = i;
            p.push(a[i]);
        }
        tree[0] = 0x3f3f3f;
        rep(i, 1, n)
        {
            scanf("%d", &tree[i]);
            if (tree[i] > tree[i - 1])//数量前缀最小值
                tree[i] = tree[i - 1];
        }
        int ans2 = tree[1];
        int last = 0x3f3f3f, last_num = 0;//last表示,最后一次取得的id是多少,last_num表示取了多少次
        while (1)
        {
            ll num = p.top().num;
            int id = p.top().id;
            if (last < id)//表示如果最后一次取得的值小于当前id的值,last已经被取完,所以id不能取
                p.pop();
            else
            {
                if (last_num >= tree[id])//数量少的也取不了
                    p.pop();
                else
                {
                    __int128 a = tree[id] - last_num;
                    __int128 b = num;
                    ans += a * b;
                    last_num = tree[id];
                    last = id;
                }
            }
            if (id == 1)
                break;
        }
        printf("Case #%d: %d ", ++cnt, ans2);
        print(ans);
        printf("\n");
    }
    return 0;
}

你可能感兴趣的:(牛客网暑假多校训练营)