首页 > 其他分享 >【题解】Educational Codeforces Round 143(CF1795)

【题解】Educational Codeforces Round 143(CF1795)

时间:2023-09-09 19:44:05浏览次数:42  
标签:Educational CF1795 int 题解 scanf pos le 怪物 res

A.Two Towers

题目描述:

有 \(a,b\) 两座由红蓝色方块垒成的塔,其中 \(a\) 的高度为 \(n\) ; \(b\) 的高度为 \(m\) ,用 R 代表红色;用B代表蓝色。

你可以多次把其中一座顶端的方块移到另一座的顶端(可以不移动)。问有没有一种方法可以使两座塔中均没有连续的同颜色方块。

题目分析:

可以先全部将方块移动到一座塔上,这样我们的一种塔的方案数就相当于选择一个分界点将这一个序列分成两半。
所以有解显然当且仅当仅有一个位置,使得这个位置有连续两个相同颜色的数,或者所有颜色都不连续。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
char s[N],t[N];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%d",&T);
	while(T--){
		int n,m;scanf("%d%d",&n,&m);
		scanf("%s%s",s+1,t+1);
		for(int i=1; i<=m; i++)	s[n+i] = t[m - i + 1];
		int cnt = 0;
		for(int i=1; i<=n+m; i++){
			int pos = i;
			while(s[pos] == s[i] && pos <= n + m) ++pos;
			--pos;
			if(pos - i + 1 == 2)	++cnt;
			if(pos - i + 1 > 2)		cnt = 2; 
		}
		if(cnt <= 1)	printf("YES\n");
		else	printf("NO\n");
	}
	return 0;
}

B.Ideal Point

题目描述:

在一条数轴上,有 \(n\) 条线段和一个点 \(k\),问能否删去若干条线段使得 \(k\) 的被覆盖次数比其他所有点的被覆盖次数都大。

题目分析:

显然就是要让点 \(k\) 覆盖次数变多不可能,就是要让其他点的覆盖次数减少。
也就是如果一个线段没有覆盖了 \(k\),那么将它删去一定是更优的,而如果一个线段覆盖了 \(k\) 那么将它删除一定是不会更优的。
删完了,判断一下即可。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
int cnt[N];
int main(){
	int T;scanf("%d",&T);
	while(T--){
		int n,k;scanf("%d%d",&n,&k);
		for(int i=1; i<=n; i++){
			int l,r;scanf("%d%d",&l,&r);
			if(k < l || k > r)	continue;
			for(int j=l; j<=r; j++)	cnt[j]++;
		}
		bool flag = true;
		for(int i=1; i<=50; i++){
			if(i == k)	continue;
			if(cnt[i] >= cnt[k])	flag = false;
		}
		if(flag)	printf("YES\n");
		else	printf("NO\n");
		for(int i=1; i<=50; i++)	cnt[i] = 0;
	}
	return 0;
}

C.Tea Tasting

题目描述:

有 \(n\) 个人和 \(n\) 杯茶,第 \(i\) 个人每次会喝 \(b_i\) 毫升的茶。第 \(i\) 杯茶有 \(a_i\) 毫升。总共会喝 \(n\) 轮茶,第 \(j\) 轮第 \(i\) 个人会尝试喝第 \(i+1-j\) 杯茶。喝的量为 \(\min(a_{i+1-j},b_i)\) 毫升,并且使 \(a_{i+1-j}\) 减少 \(\min(a_{i+1-j},b_i)\) 。问 \(n\) 轮后每个人喝了多少毫升茶。
\(1 \le n\le 2\times 10^5\)

题目分析:

因为茶会有喝完的时候,所以如果我们直接对于每一个人去统计茶对他产生的贡献,就很不好做。
所以考虑对于每一杯茶,统计它对每一个人造成的贡献,对于第 \(i\) 杯茶,在第 \(j\) 轮喝它的人就是 \(i + j - 1\),所以可以考虑直接二分得到这一杯茶在哪个人那里被喝没了,这样对于这个人前面喝过这个茶的人都完全喝了 \(b\) 毫升,而对于这个人就是减一下就知道他喝了多少毫升。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
const int INF = 1e18+5;
int a[N],b[N],cnt[N],res[N];
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%lld",&T);
	while(T--){
		int n;scanf("%lld",&n);
		for(int i=1; i<=n; i++)	scanf("%lld",&a[i]);
		for(int i=1; i<=n; i++)	scanf("%lld",&b[i]),b[i] += b[i-1];
		b[n+1] = INF;
		for(int i=1; i<=n; i++){
			int pos = upper_bound(b+i,b+n+2,a[i] + b[i-1]) - b;
			cnt[i]++,cnt[pos]--;
			res[pos] += a[i] - (b[pos-1] - b[i-1]);
		}
		for(int i=1; i<=n; i++)	cnt[i] += cnt[i-1];
		for(int i=1; i<=n; i++)	printf("%lld ",cnt[i] * (b[i] - b[i-1]) + res[i]);
		printf("\n");
		for(int i=1; i<=n+1; i++)	cnt[i] = 0,b[i] = 0,res[i] = 0;
	}
	return 0;
}

D.Triangle Coloring

题目描述:

一共有 \(n\) 条边( \(n\) 是 \(6\) 的倍数) ,每连续三条边构成一个三角形(比如若 \(n=6\) 那么第 \(1,2,3\) 条边构成一个三角形, \(4,5,6\) 条边构成另一个三角形 ),每条边有一个权值 \(w_i\) ,现在你可以把所有三角形的顶点涂成蓝色或红色,且要求有 \(\frac{n}{2}\) 个蓝点和 \(\frac{n}{2}\) 个红点,定义总权值为所有两端点颜色不同的边的权值和,问有多少种涂色方案可以使总权值最大(答案模 \(998244353\) )。
\(6 \le n \le 4 \times 10^5,1 \le w_i \le 1000\)

题目分析:

对于一个三角形,因为边权全部大于 \(0\),所以全部染成蓝色和红色一定不是权值和最大的答案,而染成蓝蓝红和红红蓝最优的方案数显然是一样的,就是让某两条边的边权之和最大的方案数。
要求染一半的红点一半的蓝点,本质上就是选一半的三角形染蓝蓝红另一半的三角形染红红蓝,而对于每一种钦定每个三角形染色方案的方式,其实际方案数为定值,所以只需要求出选择染色方案的方案数即可。
而选择的方案数显然是:

\[\binom{n \div 3}{n \div 6} \]

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 998244353;
const int N = 1e6+5;
int a[N],fac[N],inv[N];
int power(int a,int b){
	int res = 1;
	while(b){
		if(b & 1)	res = res * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return res;
}
int binom(int n,int m){
	if(n < m || n < 0 || m < 0)	return 0;
	return fac[n] * inv[m] % MOD * inv[n-m] %MOD;	
}
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n;scanf("%lld",&n);
	fac[0] = 1;
	for(int i=1; i<=n; i++)	fac[i] = fac[i-1] * i % MOD;
	inv[n] = power(fac[n],MOD-2);
	for(int i=n-1; i>=0; i--)	inv[i] = inv[i+1] * (i+1) % MOD;
	int res = 1;
	for(int i=1; i<=n/3; i++){
		for(int j=1; j<=3; j++)	scanf("%lld",&a[j]);
		int ans = 0,cnt = 0;
		for(int j=1; j<=3; j++){
			for(int k=j+1; k<=3; k++){
				if(a[j] + a[k] > ans)	ans = a[j] + a[k],cnt = 1;
				else if(a[j] + a[k] == ans)	cnt++;
			}
		}
		res = res * cnt % MOD;
	}
	res = res * binom(n/3,n/6) % MOD;
	printf("%lld\n",res);
	return 0;
}

E.Explosions?

题目描述:

你在玩一个利用魔法杀怪物的游戏。具体地,有一排 \(n\) 个格子,第 \(i\) 个格子里有编号为 \(i\)、初始生命值为 \(h_i\) 的怪物。怪物生命值小于等于 \(0\) 则被杀死,所有怪物被杀死则游戏结束。

你有两种攻击方式:一种是普通攻击,你将消耗 \(1\) 点能量对选中的怪物进行 \(1\) 点伤害(也就是怪物的生命值减 \(1\)),你可以使用普通攻击任意次数。同时,你还可以使用恰好一次“爆炸”攻击。你想要游戏以“爆炸”攻击结束,也就是说,你会先进行若干次普通攻击(可以为 \(0\) 次),然后使用一次“爆炸”攻击结束游戏。

“爆炸”攻击的机制如下:你可以选择消耗的能量数 \(x\),然后选中一个怪物 \(i\),之后:

  • 若怪物 \(i\) 当前的生命值 \(h_i > x\),那么该怪物生命值减去 \(x\);
  • 若 \(h_i\le x\),则该怪物被击杀,同时向两旁的怪物(即第 \(i-1\) 和 \(i+1\) 只怪物,如果有且还存活的话)造成 \(h_i - 1\) 的伤害;
  • 若 \(h_i - 1\) 的伤害足以杀死第 \(i-1\) (或 \(i+1\))只怪物,即 \(h_{i-1}\le h_i - 1\)(或 \(h_{i+1}\le h_i - 1\)),那么这只怪物同样被击杀并向两旁的怪物造成 \(h_{i-1} - 1\)(或 \(h_{i+1} - 1\))的伤害。如此循环只到杀不死一直怪物为止。

你的目标就是先用普通攻击减少某些怪物的生命值或直接杀死某些怪物,然后用这样“链式”的“爆炸”攻击杀死所有怪物。注意怪物不会移动,例如第 \(i\) 只怪物和第 \(i+2\) 只怪物永远不会相邻。

你需要消耗最少的能量值来达成目标(包括普通攻击和“爆炸”攻击的),求出这个最小值。

\(1 \le n \le 10^5\)

题目分析:

考虑使用爆炸操作之前的状态一定是前面一段 \(0\) + 一个严格单峰的段 + 后面一段 \(0\)
所以可以考虑直接枚举这个峰在那里,那么就是要求前面严格单调递增后面严格单调递减,\(0\) 的问题其实可以理解为负数变成 \(0\)。
显然这两段问题是对称的,也就是解决一个问题即可,考虑解决前面的这个问题。
记 \(L[i]\) 表示让 \([1,i]\) 严格单调递增的最小操作次数,可以发现若存在 \(j < i\) 满足 \(a_j \le a_i - (i-j)\),也就是 \(a_j\) 可以在不被操作的情况下爆掉,那么 \([1,j]\) 的贡献就是 \(L[j]\) 下面直接考虑 \([j+1,i]\) 的贡献即可,显然可以找到最大的一个 \(j\) 满足上述条件。
那么该怎么求这个 \(j\) 呢,显然上述条件可以转化为 \(a_j - j \le a_i - i\),直接用单调栈就可以维护了。
对于 \([j+1,i]\) 的贡献,不妨假设 \(x \in [j+1,i]\),则这个点的操作次数就是 \(a_x - [a_i - (i-x)]\),如果放到这一整段来看的话,\(a_x\) 就是相当于区间和 \(a_i - (i-x)\) 就是一个等差数列求和,都很好维护。
要注意的细节就是,若 \(a_i - (i-x)\) 为负数,要认为是 \(0\)。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e5+5;
const int INF = 1e18+5;
int a[N],L[N],R[N],sum[N];
int get(int x,int len){
	int y = x - len + 1;
	return (x + y) * len / 2;
}
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%lld",&T);
	while(T--){
		int n;scanf("%lld",&n);
		for(int i=1; i<=n; i++)	scanf("%lld",&a[i]);
		for(int i=1; i<=n; i++)	sum[i] = sum[i-1] + a[i];
		stack<pair<int,int> > st;
		for(int i=1; i<=n; i++){
			while(!st.empty() && st.top().first > a[i] - i)	st.pop();
			int pos = 0;
			if(st.empty())	pos = 0;
			else	pos = st.top().second;
			//注意的细节:减成负的相当于减成 0 
			if(pos != 0)	L[i] = L[pos] + sum[i] - sum[pos] - get(a[i],min(i-pos,a[i]));
			else	L[i] = sum[i] - get(a[i],min(i,a[i]));
			st.push({a[i]-i,i});
		}
		reverse(a+1,a+n+1);
		for(int i=1; i<=n; i++)	sum[i] = sum[i-1] + a[i];
		while(!st.empty())	st.pop();
		for(int i=1; i<=n; i++){
			while(!st.empty() && st.top().first > a[i] - i)	st.pop();
			int pos = 0;
			if(st.empty())	pos = 0;
			else	pos = st.top().second;
			//注意的细节:减成负的相当于减成 0 
			if(pos != 0)	R[i] = R[pos] + sum[i] - sum[pos] - get(a[i],min(i-pos,a[i]));
			else	R[i] = sum[i] - get(a[i],min(i,a[i]));
			st.push({a[i]-i,i});
		}
		for(int l=1,r=n; l<=r; l++,r--)	swap(R[l],R[r]);
		int ans = INF;
		reverse(a+1,a+n+1);
		for(int i=1; i<=n; i++)	ans = min(ans,L[i] + a[i] + R[i]);
		printf("%lld\n",ans);
	}
	return 0;
}

标签:Educational,CF1795,int,题解,scanf,pos,le,怪物,res
From: https://www.cnblogs.com/linyihdfj/p/17690051.html

相关文章

  • UVA202 题解
    思路分析前言又是一道小模拟题,不过细节巨多,可以用来锻炼自己的代码能力。解法本题实际上就是模拟长除法的计算过程,其中每一步除法时都有被除数及其余数,当被除数出现重复时就表示出现循环节了。所以需要记录每一次的被除数及其在循环小数中的位置,需要判断当除数不够除,每一次补......
  • 题解 [CQOI2009] 中位数
    题目链接要想使得数字\(x\)是中位数,就必须选出\(k\)个小于\(x\)的数和\(k\)个大于\(x\)的数。我们考虑对数字附上特殊值,小于\(x\)的数赋值为\(-1\),大于\(x\)的数赋值为\(1\),\(x\)则赋值为\(0\),那么若一段包含\(x\)的连续序列的和等于\(0\),就可以说明这段连......
  • [题解] Codeforces Round 895 (Div. 3) F~G
    CodeforcesRound895(Div.3)F~GF.SellingaMenageri考虑如何让卖出的价格翻倍,那么自然是从\(i\toa_i\)。通过这样连边,我们可以发现,边集构成了基环树森林。显而易见的是,如果不考虑环,那么图就是拓扑图,按照拓扑关系跑一遍,就可以使得所有点价值最多。现在考虑环上的问题......
  • [AGC036C] GP 2 题解
    洛谷题目链接AT原题考虑构造出来的序列\(a\)的特征,因为每次会给\(a_i\)加\(1\),\(a_j\)加\(2\),所以每次操作后\(\suma_i\)会加上\(3\)。所以有\(\suma_i=3m\)。又因为每次操作只给一个数加\(1\),所以每次操作要么给序列加入一个奇数,要么使原来的一个奇数变成偶数......
  • [题解] CF29D Ant on the Tree
    CF29DAntontheTree题目知识点:LCA。题目传送门题意给定一棵以\(1\)为节点的树,再给定树的所有叶子节点的一个序列。现在执行一个操作:从\(1\)开始遍历每个节点,并返回根,要求每条边经过的次数一定为\(2\)。问是否能够使得访问节点序列中叶子节点的序列符合给定序列的条......
  • P2206题解
    题目大意:给定一些指令,计算需要多大的舞台。这是一道大模拟!!!只要遍历每次指令,然后判断是否摔倒,摔倒输出`-1`否则记录,最后求出面积就行了。最后附上代码1#include<bits/stdc++.h>2usingnamespacestd;3constintxx[]={-1,0,1,0},yy[]={0,1,0,-1};//不同......
  • CF1513C题解
    一道递推由于对于一个数x,可得x+10-x=10(废话)于是问题就变成了0+m次,然后x+m就变成0+x+m(还是废话)于是可以写一个递推。首先对于函数f(m)可分为m ≤9和m>9,然后可得出递推式结果为1或f(m-9)+f(m-10),所以我们可以直接求出结果再分解数位求值。最后贴上代码......
  • P2203题解
    题意给定一个环形01序列,每次变化时,对于每个位置,如果前一个值是1,则当前值翻转。求变化B次后的序列。思路由于B的值很大,所以如果对每一次变化进行模拟,效率非常低下。不难发现,每一次变化后的状态完全是由当前状态决定的,而N的范围很小,所以可能的状态总数2^N也不是很大......
  • CF812B题解
    康了康唯一的题解,说没必要用DP,我就给出DP的解法。这其实是道水题,唯一的坑是有可能楼上没有开的灯,坑了我们机房的一堆人(WAontest4),剩下的就是DP。我们用a[n][0],表示第n层的第一个房间,用a[n][1],表示第n层的最后一个房间。这里提供一个收集型的写法。所以可得状态转移......
  • CF1690E题解
    ##主要题意:有$n$个礼物,要两两合并,然后除以$k$最后求和最大。##思路:先加入每个数除以$k$的商(单独组成$k$的个数),然后全部$\bmod\k$存入数组,排序,最后双指针一个前一个后求两个余数可以大于等于$k$的两个礼物。##代码:```cpp#include<bits/stdc++.h>usingname......