CFR-850-Div-1解题报告

比赛传送门

A. Monsters (easy version)

题意:有 n 个怪物,每个有 ai 滴血,每次可以选择一个怪物减一滴血,也可以“让所有怪物减一滴血,且如果杀死怪物则重复操作”。其中,第二种攻击只能使用一次。问杀死所有怪物,使用第一种攻击的最小次数。

显然要让第二种攻击发挥最大效果,就要让所有怪物的血量连续,一次杀掉所有怪物。可以从小到大排序后依次考虑每个怪物,如果扫到一个怪物的血量比目前连续的最大血量高,则攻击到最大血量+1

By Mr_Eight

int n,a[200002];
int main() {
	F(adsf,1,read()){
		cin>>n;
		F(i,1,n)read(a[i]);
		sort(a+1,a+n+1);
		int now=0;
		ll ans=0;
		F(i,1,n){
			++now;
			now=min(now,a[i]);
			ans+=a[i]-now;
		}
		write(ans,'\n');
	}
	return 0;
}

B. Letter Exchange

题意:有 n 个人和 3 种卡片,每种各 n 张。每人初始有 3 张卡片,每次操作可以让两人各处一张卡片互换。问让每人都集齐 3 种卡片的最小操作数,输出方案。

可以将每个人拥有卡片的情况存到 vector 里,其中 v[i][j] 存所有多了 i 且少了 j 的人的编号。对于 000 类型的人,在 v[0][1]v[0][2] 中各出现一次即可。

显然应该尽可能多地让 v[i][j]v[j][i] 的人互换,且所有互换相互独立,直接处理即可。剩下的人一定不存在长度为 2 的交换环,所以考虑长度为 3 的,即 v[i][j],v[j][k],v[k][i]。这里只需要两次交换即可归位,且与外界独立。将两个方向的交换环分别处理即可。由于一共只有三种卡片且每种 n 张,经过这两步一定可以归位。

By maroonrk

const string win="win";

void slv(){
	int n;cin>>n;
	vi g[3][3];
	rep(i,n){
		string s;cin>>s;
		int cnt[3]{};
		for(auto c:s){
			cnt[win.find(c)]++;
		}
		vi u,v;
		rep(j,3)if(cnt[j]==0){
			v.pb(j);
		}else{
			rep(_,cnt[j]-1)u.pb(j);
		}
		assert(si(u)==si(v));
		rep(j,si(u)){
			g[u[j]][v[j]].pb(i);
		}
	}
	vc<tuple<int,int,int,int>> ans;
	rep(i,3)rep(j,i){
		while(si(g[i][j])&&si(g[j][i])){
			auto x=g[i][j].back();g[i][j].pop_back();
			auto y=g[j][i].back();g[j][i].pop_back();
			ans.eb(x,y,i,j);
		}
	}
	auto work=[&](int i,int j,int k){
		assert(si(g[i][j])==si(g[j][k]));
		assert(si(g[i][j])==si(g[k][i]));
		while(si(g[i][j])){
			auto x=g[i][j].back();g[i][j].pop_back();
			auto y=g[j][k].back();g[j][k].pop_back();
			auto z=g[k][i].back();g[k][i].pop_back();
			ans.eb(x,y,i,j);
			ans.eb(y,z,i,k);
		}
	};
	work(0,1,2);
	work(0,2,1);
	print(si(ans));
	for(auto [x,y,i,j]:ans){
		cout<<x+1<<" "<<win[i]<<" "<<y+1<<" "<<win[j]<<"\n";
	}
}

C. Monsters (hard version)

题意:杀敌的方式与 A 题相同,区别在于对于每个 k,要求出假设只有前 k 个怪物时的最小次数。

做法一

可以倒序考虑。首先像第一题一样处理出所有 n 个怪物的方案,并把“没有发挥作用的”扔到 multiset 里。

从后往前依次删除怪物,维护当前“连续”到多少(假设为 x),以及所有“发挥作用”的怪物血量之和(sum)。答案即为 sumx(x+1)2

当删除一个怪物时,如果存在“没有发挥作用的”血量值相同的怪物,则直接从 multiset 里删除一个即可,答案不会改变。否则,这个怪物是发挥作用的怪物。将其删掉后 sum 减去该血量。x 是否要减去 1 呢?如果该血量之后存在一个最近的没有发挥作用的怪物,则其会变为发挥作用(从 multiset 中删掉),从而导致连续的长度不变。如果该血量之后所有怪物都已经发挥作用,则连续的长度会减小(x 减去 1)。

By fireskydd

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k,a[200004];ll b[200004];
multiset<int>s;ll ans;
void sol(){
	scanf("%d",&n),s.clear(),ans=0,k=0;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),s.insert(a[i]);
	for(;;){
		k++;auto it=s.lower_bound(k);
		if(it==s.end()){k--;break;}
		ans+=*it,s.erase(it);
	}
	for(int i=n;i;i--){
		b[i]=ans-(ll)k*(k+1)/2;
		auto it=s.lower_bound(a[i]);
		if(it!=s.end()&&*it==a[i])s.erase(it);
		else{
			ans-=a[i];
			if(it==s.end())k--;
			else ans+=(*it),s.erase(it);
		}
	}
	for(int i=1;i<=n;i++)printf("%lld ",b[i]);puts("");
}
int main(){
	int tc;scanf("%d",&tc);
	while(tc--)sol();
}

做法二

posted @   曹轩鸣  阅读(32)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起