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

I-Interesting Computer Game

题意:给出n对数,对于每一对,其中如果有没有选过的数那么就可以选,就可以选择那一个数,ans++。需要求最大的ans

思路:队友很强,思路秒出,十分钟代码,一发ac 可以把这些数对想成一条边上的两个点,可以想像一下如果我要最多的选择这上面的点,假如这些点构成的边是一棵树的话,我们最多在这n个点中能选择的只有n-1个点。如果有成环的话,不论是什么环,都可以让这n个点都选得到,画一个图看一看就很清楚了。数据需要离散化一下,然后用并查集来存储这些连通图,增加一个 判断是否有环连通图大小 的的数组,不断维护就可以了。

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

int _, n, cas;
map <int, int> mp;
bool huan[200005];
int fa[200005], siz[200005];

int find(int x) {
	if(x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}

int main() {
	scanf("%d", &_);
	while(_--) {
		int now = 0;
		mp.clear();
		memset(huan, 0, sizeof(huan));
		for(int i = 1;i <= 200000; i++) fa[i] = i, siz[i] = 1;
		
		scanf("%d", &n);
		for(int i = 1;i <= n; i++) {
			int u, v;
			scanf("%d %d", &u, &v);
			if(!mp[u]) mp[u] = ++now;
			if(!mp[v]) mp[v] = ++now;
			int fu = find(mp[u]);
			int fv = find(mp[v]);
			if(fu == fv) {
				huan[fu] = 1;
			}
			else {
				fa[fu] = fv;
				siz[fv] += siz[fu];
				if(huan[fu]) huan[fv] = 1;
			}
		}
		
		long long ans = 0;
		for(int i = 1;i <= now; i++) {
			int fi = find(i);
			if(fi != i)continue;
			ans += siz[fi];
			if(!huan[fi]) ans--;
		}
		
		printf("Case #%d: %lld\n",++cas, ans);
	}
}

K-Kabaleo Lite

题意:给n道菜的利润,还有每道菜的库存。每个客人每道菜最多只能吃一盘,并且必须从第一道菜开始吃,之后如果要继续吃,必须是连续的菜,比如吃完第一种菜,想吃第二盘的话就必须吃第二种。求最多可以招待多少个客人,并且求出客人最多的情况下的最大利润。

思路:前缀和求出菜的利润,可以知道最大招待客人的数量就是第一道菜的份数,所以在处理b数组的时候可以往后取min,比如样例中 4 2 1 2经过处理就变成 4 2 1 1,把无效的数据抹掉。拿第二个样例举例经过上述变换之后数据为

3 1 4 2
4 2 1 1

当四个人只吃第一份食物时可以赚12元,然后往后看第二份食物,分多少个吃到第二份很亏,因为吃的又多还没得只吃第一份赚的多,再看第三份,把只吃第一份的人分一个吃到第三份来,就是 3 ∗ 3 + 4 ∗ 1 = 13 3*3+4*1=13 33+41=13 的利润了,再往后吃就又没有当前赚了。

由这个步骤我们可以知道,在往后遍历时,如果有比之前最大的利润更大的时候我们就可以尽量选择这个更大利润的菜来吃,又因为有库存的限制因素,那么就把之前的人减去这个更多的库存,加到最终利润里面就可以了。 利用单调栈来处理这个题。对了,这题的数据范围爆 long long了,需要使用 __int128 来存储答案。

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

int _, n;
long long a[100005], b[100005];
stack <pair <long long, long long> > sta;

inline void write(__int128 x) {
	if(x < 0) {
		putchar('-');
		x = -x;
	}
	if(x>9) write(x/10);
	putchar(x%10+'0');
}

int main() {
	int cas = 0;
	scanf("%d", &_);
	while(_--) {
		scanf("%d", &n);
		for(int i = 1;i <= n; i++) {
			scanf("%lld", &a[i]);
			a[i] = a[i] + a[i-1];
		}
		b[0] = 1e9;
		for(int i = 1;i <= n; i++) {
			scanf("%lld", &b[i]);
			b[i] = min(b[i], b[i-1]);
		}
		
		sta.push(make_pair(a[1], b[1]));
		for(int i = 2;i <= n; i++) {
			pair <long long, long long> t = sta.top(); sta.pop();
			if(a[i] > t.first) {
				if(b[i] == t.second) sta.push(make_pair(a[i], b[i]));
				else {
					t.second -= b[i];
					sta.push(t);
					sta.push(make_pair(a[i], b[i]));
				}
			}
			else sta.push(t);
		}
		
		__int128 ans = 0;
		while(!sta.empty()) {
			__int128 u = sta.top().first, v = sta.top().second;
			sta.pop();
			ans += u*v;
		}
		
		printf("Case #%d: %lld ", ++cas, b[1]);
		write(ans); puts("");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值