Codeforces训练

C. Circle of Monsters

题意:

你正在玩另一个电脑游戏,现在你必须杀死怪物。这些怪物站在一个圆圈中,顺时针编号从1到n。最初,第i个怪物拥有ai生命值。
你可以射击怪物来杀死他们。每次射击只需要一颗子弹,并降低目标怪物的生命值1(对其造成1点伤害)。此外,当某些怪物i的生命值变为0或小于 0时,它便会死亡并爆炸,并对下一个怪物造成bi伤害(如果i如果下一个怪物已经死了,那么什么也不会发生。如果爆炸杀死了下一个怪物,它也会爆炸,在它之后破坏怪物,并可能触发另一次爆炸,以此类推。你必须计算出杀死圆圈内所有n个怪物所需发射的最小子弹数。

首先我们要想, 如何用最少的子弹造成最大的伤害, 即尽可能的利用爆炸伤害, 所以从某个怪物开始, 每次射击的都是它后面未被炸死的怪物, 所以问题就变成了,从哪一个怪物开始射击, 所以我们枚举从每一个怪物开始,最开始我们要预处理出来利用上一个怪物的爆炸伤害, 我们还需要补几枪

#include<bits/stdc++.h>
#define x first
#define int long long 
#define ll long long 
#define y second
#define gg exit(0);
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define db printf("debug\n");
const int N = 5e5 + 10;
using namespace std;
typedef pair<ll, ll>PII;
int T;
long long a[N], b[N], n, c[N];

void solve()
{	
	cin >> n;

	for(int i = 1; i <= n; i ++ ) cin >> a[i] >> b[i];


	long long  sum = 0;
	for(int i = 1; i <= n; i ++ )
	{
		if(i == 1) c[i] = max((ll)0, a[i] - b[n]); 
		else c[i] = max((long long )0, a[i] - b[i - 1]);

		  sum += c[i];
	}
	long long  ans = 1e18;
	for(int i = 1; i <= n; i ++ )
		ans = min(ans, sum - c[i] + a[i]); //因为从这个怪物开始, 所以这个怪物受到的爆炸伤害不能计算在内

	cout << ans << '\n';
}
signed main()
{
	io;
	//T = 1;
	cin >> T;
	while(T -- )
	solve();
}

C. Ilya and Sticks

到了晚上,比赛结束后,伊利亚觉得很无聊,他真的很想最大化。他记得他有一套棍子和一件乐器。每根棍子的特点是长度为里。

伊利亚决定用树枝做一个长方形。由于一时的心血来潮,他决定用一种最大化矩形面积的方式来制作矩形。每根棍子最多用来制作一个矩形,可能有一些棍子没有被使用。不允许弯曲木棍。

如果观察到以下属性,长度为a1、a2、a3和a4的棒可以构成一个矩形:

a1≤a2≤a3≤a4

a1, = a2

a3 = a4

矩形可以由长度为,例如,3 3 3 3或2 2 4 4的棍子组成。一个矩形不能由,例如,棍棒5 5 5 7。

伊利亚也有一个工具可以缩短棍子的长度。这些棍子是由一种特殊材料制成的,所以每根棍子的长度最多可以减少1。例如,长度为5的棍子可以保持这个长度,也可以转换成长度为4的棍子。

你必须回答这个问题——如果用可用的木棒制作矩形,Ilya可以用一个文件得到的矩形的最大总面积是多少?

首先要面积最大,我们肯定会先用长的木棍拼在一起, 所以从大到小排序肯定没问题, 然后每四个一处理, 但是这里就出了点问题, 不一定是每四个一处理, 只要相邻两个能通过工具凑成一对, 就可以与其他对凑成矩形, 所以我们排序后,要每两个一处理, 然后放入数组, 最后一起计算面积

#include<bits/stdc++.h>
#define x first
#define ll long long 
#define y second
#define gg exit(0);
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define db printf("debug\n");
const int N = 5e5 + 10;
using namespace std;
typedef pair<ll, ll>PII;
int T;
ll n, a[N];
bool cmp(int a, int b)
{
	return a > b;
}
void solve()
{	
	 cin >> n;
	 vector<ll>b;
	 for(int i = 1; i <= n; i ++ )
	 	cin >> a[i];
	 sort(a + 1, a + 1 + n, cmp);
	 ll s = 0;
	 for(int i = 1; i <= n - 1; i ++)
	 	if(abs(a[i] - a[i + 1]) <= 1 )  //符合条件
	 		b.push_back(a[i + 1]), i ++;  //放入数组

	 for(int i = 0; i < (int)b.size() - 1; i += 2 )  //计算面积
	 	s += b[i] * b[i + 1];

	 cout << s << '\n';
}
signed main()
{
	io;
	T = 1;
	// cin >> T;
	while(T -- )
	solve();
}

C. New Year Book Reading

题意:

kn要去读书,一共n本书,标号为1-n,每本书的重量为wi.
kn家没有书橱,只好将书一本放在一本上面组成一列,他现在想要读第x本书,他要这么做。
1.举起在x上面的所有的书
2.然后将x书从堆里面拿出来
3.将x上面的书不改变顺序地放回去
4.将x放到最上面
他决定在m天内读书,在第j天,他会读第bj (1 ≤ bj ≤ n)本书. 他取出书的方式就如同上面说的那样。
做了这样的计划之后,他突然发现作为一个一年级学生他举起想读的书上的书实在是太重了。所以他决定改变书堆的顺序(也就是初始的书是怎么堆的),从而减少总归抬起书的重量。
注意:他将x书抽出来的时候不算进抬起的重量。

首先我们要确定每本书的初始位置, 我们不难知道, 通过一定的操作之后, 不论你书怎么放, 他的位置一定是固定的, 因为每次我们都要把那本书放到顶部, 所以当每本书都被放到顶部后, 他的位置就已经固定了, 与初始位置无关, 但是在每本书第一次本拿时, 与初始位置有关, 所以我们要根据每本书第一次本拿的位置, 确定每本书的初始位置
确定完初始位置后, 然后模拟即可

#include<bits/stdc++.h>
#define x first
#define ll long long 
#define y second
#define gg exit(0);
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define db printf("debug\n");
const int N = 5e5 + 10;
using namespace std;
typedef pair<ll, ll>PII;
int T;
ll n, w[N], b[N], m;
stack<int>s;
vector<int>a, c;
bool st[N];
void solve()
{	

	cin >> n >> m;

	for(int i = 1; i <= n; i ++ ) cin >> w[i];

	for(int i = 1; i <= m; i ++ ) cin >> b[i];

	for(int i = 1; i <= m; i ++ )
		if(!st[b[i]])  //这本书第一次出现
		{
			a.push_back(b[i]);
			st[b[i]] = true;
		}

	for(int i = (int)a.size() - 1; i >= 0; i -- ) s.push(a[i]);  //初始位置

	int sum = 0;
	for(int i = 1; i <= m; i ++ )
	{
		while(s.top() != b[i])  //将需要看的书的上面的书,全部取出, 并放入c数组,以便于后面再放回
		{
			sum += w[s.top()];  //计算答案
			c.push_back(s.top());
			s.pop();
		}
		s.pop();
		for(int i = (int)c.size() - 1; i >= 0; i -- ) s.push(c[i]);
		s.push(b[i]);   //别忘记把要看的这本书放在顶部
		c.clear(); //清空
	}
	cout << sum << '\n';
}
signed main()
{
	io;
	T = 1;
	// cin >> T;
	while(T -- )
	solve();
}

C. New Year and Rating

很多OJ有自己的比赛和等级,比赛分为A组比赛和B组比赛,规定当等级小于等于1899时,只能参加B组比赛,当等级大于等于1900时,只能参加A组比赛。现在给出某人若干次比赛的数据,数据包括他参加获得的等级的变化和参加的组别,在不知道初始等级的情况下,计算这个人参加完这若干次比赛后最高的等级是多少,计算结果必须是可行的,即不能出现不符合组别等级参赛的情况。

首先我们要确定初始分数, 只要确定了初始分数, 对于最终分数模拟即可求出, 但是如何确定最大的初始分数呢? 首先我们可以用两个变量表示初始分数的上下界, 如果下界大于上界就无解, 如果上界为正无穷, 说明最高分无上限, 然后通过每一次比赛, 可以确定上下界, 如果我下一场参加的是 B B B 组的比赛, 说明我这场比赛打完后我的得分的下界一定是小于等于1899的, 所以我们可以得到 r + c [ i ] < = 1899 r + c[i] <= 1899 r+c[i]<=1899 , 如果下一场参加的是 A A A 组的比赛, 那么我们打完这场比赛后一定是大于 1900 1900 1900 的, l + c [ i ] > = 1900 l + c[i] >= 1900 l+c[i]>=1900,这样我们就可以通过每一场比赛确定上下界了, 还要注意, 当第一场是A组比赛时, 我们需要把下界赋值为 1900 1900 1900, 否则赋值上界为 1899 1899 1899

#include<bits/stdc++.h>
#define x first
#define ll long long 
#define y second
#define gg exit(0);
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define db printf("debug\n");
const int N = 5e5 + 10;
using namespace std;
typedef pair<ll, ll>PII;
int T;
int n, c[N], d[N];
void solve()
{	
	cin >> n;

	for(int i = 1; i <= n; i ++ ) cin >> c[i], c[i] += c[i - 1], cin >> d[i];


	int l = -1e9, r = 1e9;
	if(d[1] == 1)
		   l = 1900;
	else
		r = 1899;
	for(int i = 1; i < n; i ++ )
		if(d[i + 1] == 1)
			l = max(l, 1900 - c[i]);  //更新下界
		else r = min(r, 1899 - c[i]);  //更新上界

	if(l > r) cout << "Impossible\n";
	else if(r == 1e9 ) cout << "Infinity\n";
	else cout << r + c[n] << '\n';
}

signed main()
{
	io;
	T = 1;
	// cin >> T;
	while(T -- )
	solve();
}

C. Anton and Fairy Tale

题意: 去过很多西方国家的Hxx1喜欢给我们讲他的旅行见闻 有一天他讲了他与外国人谈笑风生时听到的故事
“很久很久以前,有一位国王突发奇想,想建一座大谷仓。 将所有的谷子都存在这个谷仓里。工匠们干了三天三夜,终于完成了,
然和这个谷仓有一个缺口,所以每天都会有麻雀来吃谷子…。 简单的说吧:有一个容量为n的谷仓,每天早晨会有m个单位的谷子存进去……
‘那么谷仓没有那么多空间呢?’ ‘too simple!当然是装满了,剩下的就倒掉了。’
然后第i天就会有i个麻雀来吃谷子,每只麻雀一天会吃掉一个单位的谷子。。。 “
w222222s不想听这个故事了,他想知道谷仓什么时候会变成空的?请你帮他写个程序判断那将会是第几天?

分析: 如果 n < = m n <= m n<=m, 那么当 i = n i = n i=n 时, 谷仓就变空了, 如果当 n > m n > m n>m时, 在 1 1 1 ~ m m m, 谷仓的谷子是不会变的, 然后从第 m + 1 m + 1 m+1 天开始, 谷仓的谷子每天减少1,2,3,4,5…,
所以我们可以推出 n - m <= (1 + x) * x / 2, 这里 n − m n - m nm 是因为每天麻雀吃的是 m + x m + x m+x, (这里 m + x m + x m+x 是指的第 m + x m + x m+x 天, 所以我们二分找到答案即可)

#include<bits/stdc++.h>
#define x first
#define int long long 
#define y second
#define gg exit(0);
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define db printf("debug\n");
const int N = 5e5 + 10;
using namespace std;
int T;
int n, m;
void solve()
{	
	cin >> n >> m;

	if(n <= m)
	cout << n << '\n';
	else 
	{
		int l = 0, r = 2e9; //左右边界 
		while(l < r)  //二分
		{
			int mid = (l + r) / 2;
			if((mid * (mid + 1)) / 2 >= (n - m)) r = mid;
			else l = mid + 1;
		}
		cout << l + m << '\n';
	}
}	
signed main()
{
	io;
	T = 1;
	// cin >> T;
	while(T -- )
	solve();
}

C. New Year and Permutation

题意:回忆一下,排列是一个由n个不同的整数组成的数组,从1到n按任意顺序排列。例如,[2,3,1,5,4]是一个排列,但[1,2,2]不是一个排列(2在数组中出现两次),而[1,3,4]也不是一个排列(n=3但数组中有4)。

序列A是序列b的子段,如果序列b可以通过删除开头的几个(可能是零或全部)元素和结尾的几个(可能是零或全部)元素来获得序列A。我们将子段表示为[l,r],其中l,r是两个1≤l≤r≤n的整数。表示从序列开始的1−1个元素和结束的n−r个元素被删除的子段。

对于置换p1,p2,…,pn,我们定义一个框架线段为子线段[l,r],其中max{pl,pl+1,…,pr}−min{pl,pl+1,…,pr}=r−l。例如,对于置换(6,7,1,8,5,3,2,4),它的一些框段是:[1,2],[5,8],[6,7],[3,3],[8,8]。特别地,子线段[i,i]总是一个有框架的线段,对于任意i(包括1和n)。

我们定义一个排列p的幸福度为1≤l≤r≤n的对(l,r)的数目,并且[l,r]是一个有框的线段。例如,排列[3,1,2]的幸福度为5:除[1,2]外的所有片段都是框架片段。

给定整数n和m, Jongwon想要计算长度为n的所有排列的幸福度之和,对质数m取模。注意,存在n!(n的阶乘)长度为n的不同排列。

对于这题, n n n 很大, 所以我们要计算贡献, 首先我们需要找到合法区间的数量,设合法区间的长度为 l e n len len, 那么从 1 1 1 ~ n − l e n + 1 n-len +1 nlen+1, 一共有 n − l e n + 1 n - len + 1 nlen+1 个, 然后内部顺序有 ! l e n !len !len 种, 外部有 ! ( n − l e n ) !(n - len) !(nlen) 种, 所以我们枚举长度, 答案就出来了
a n s = ∑ i = 1 n ( n − i + 1 ) × ! i × ! ( n − i ) ans = \sum_{i=1}^n(n - i + 1) × ! i × !(n - i) ans=i=1n(ni+1)×!i×!(ni)

#include<bits/stdc++.h>
#define x first
#define int long long 
#define y second
#define gg exit(0);
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define db printf("debug\n");
const int N = 5e5 + 10;
using namespace std;
int T;
int n, m;
int fac[N];
void solve()
{	
	cin >> n >> m;

	fac[0] = 1;
	fac[1] = 1;
	for(int i = 2; i <= n; i ++ ) fac[i] = fac[i - 1] * i % m;  //预处理阶乘

	int res = 0;
	for(int i = 1; i <= n; i ++ )  //一定要记得多取模, 不然爆longlong了
		res = ((res + ((((n - i + 1) * (n - i + 1) % m) * fac[i] % m )* fac[n - i]) % m ) % m);
	cout << res % m << '\n';
}	
signed main()
{
	io;
	T = 1;
	// cin >> T;
	while(T -- )
	solve();
}

D. Vupsen, Pupsen and 0

题意:Vupsen和Pupsen被赋予一个整数数组。因为Vupsen不喜欢数字0,所以他把数组中所有等于0的数字都扔掉了。结果,他得到了一个长度为n的数组a。

相反,普普森喜欢数字0,当他看到数组中没有0时,他感到不安。为了让Pupsen高兴起来,Vupsen决定提出另一个长度为n的数组b,使得∑ni=1ai⋅bi=0。因为Vupsen不喜欢数字0,数组b不能包含等于0的数字。另外,数组中的数字不能太大,因此它们的绝对值之和不能超过 1 0 9 10^9 109。请帮助Vupsen找到这样的数组b!

分析: 当 n n n 为偶数时, 两两配对即可, 例如 一个数为 a a a, 另一个数为 b b b, 则 b b b 数组为 − b -b b, a a a, 当 n n n 为奇数时, 我们只要处理掉前 3 3 3 个数, 剩下的就和偶数的情况一样了, 我们可以把三个数中的两个数看作一个整体, 然后当做两个数处理, 前提是这两个数的和不为 0 0 0, 例如,这三个数分别为 a , b , c a, b, c a,b,c, 我们可以是 a + b , c a + b, c a+b,c, 或者 a + c , b a + c, b a+c,b, 或者是 b + c , a b + c, a b+c,a, 其对应的 b b b 数组是 − c , − c , a + b -c, -c, a + b c,c,a+b, 或者 − b , − b , a + c -b, -b, a + c b,b,a+c, 或者是 − a , − a , b + c -a, -a, b + c a,a,b+c, 这三种情况并不是全都存在的, 要判断是否为 0 0 0,

#include<bits/stdc++.h>
#define x first
#define int long long 
#define y second
#define gg exit(0);
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define db printf("debug\n");
const int N = 5e5 + 10;
using namespace std;
int T;
int n, m;
int a[N], b[N];
void solve()
{	
	cin >> n;

	for(int i = 1; i <= n; i ++ ) cin >> a[i];


	if(!(n & 1))  //偶数情况
	{
		for(int i = 1; i <= n; i ++ )  
		{
			if(i % 2) cout << -1 * a[i + 1] << " ";
			else cout << a[i - 1] << " ";
		}
	}
	else
	{
		int x = a[1], y = a[2], z = a[3];  //处理前三个数
		if(x + z != 0)  
			cout << -1 * y << " " << x + z << " " << -1 * y << ' ';
		else if(x + y != 0)
			cout << -1 * z << " " << -1 * z << " " << x + y << ' ';
		else cout << y + z << " " << -1 * x << " " << -1 * x << ' ';

		for(int i = 4; i <= n; i ++ )
		{
			if(!(i % 2)) cout << -1 * a[i + 1] << " ";
			else cout << a[i - 1] << " ";
		}
	}
	cout << '\n';
}	
signed main()
{
	io;
	// T = 1;
	cin >> T;
	while(T -- )
	solve();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广西小蒟蒻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值