补题

2020牛客暑期训练营

第二场B题

在这里插入图片描述

题意:输入n个整数点,求这些点中最多有多少个点与原点共圆,答案要加上原点

思路:因为三点确定一个圆,所以枚举出所有的圆心,找出出现次数最多的圆心再加上1即为答案

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
typedef pair<double, double> p;
map<p, int> a;
double x[2003], y[2003];
int main()
{
	int n, i, j, ans = -1;
	cin >> n;
	double a1, b1, c1, a2, b2, c2;
	for(i = 1; i <= n; i++)
	{
		cin >> x[i] >> y[i];
	}
	for(i = 1; i < n; i++)
	{
		p p1(x[i],y[i]);
		a.clear();
		for(j = i + 1; j <= n; j++)
		{
			p p2(x[j],y[j]);
			if(p2.second * p1.first - p2.first * p1.second == 0)	continue;
			a1 = 2 * (p1.first - 0);
			b1 = 2 * (p1.second - 0);
			c1 = p1.first * p1.first + p1.second * p1.second; 
			a2 = 2 * (p2.first - p1.first);
			b2 = 2 * (p2.second - p1.second);
			c2 = p2.first * p2.first + p2.second * p2.second - p1.first * p1.first - p1.second * p1.second;
			p o;
			o.first = ((c1 * b2) - (c2 * b1)) / ((a1 * b2) - (a2 * b1));
			o.second = ((a1 * c2) - (a2 * c1)) / ((a1 * b2) - (a2 * b1));
			a[o]++;
			if(a[o] > ans)	ans = a[o];
		}
	}
	cout << ans + 1;
	return 0;
}
第二场C题

在这里插入图片描述

题意:给出n个结点的树,求用最少的“链”来覆盖所有的边,输出所需最小的链数和这些链

思路:每两个叶结点需要一条链来覆盖他们,用dfs求出叶结点的数量z,最小链数为叶节点总数z/2

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
vector<int> v[200005];
int z = 0, ans[200005];
void dfs(int x, int y)
{
	int i;
	for(i = 0; i < v[x].size(); i++)
	{
		if(v[x][i] != y)
		{
			dfs(v[x][i],x);
		}
	}
	if(v[x].size() == 1)
	{
		z++;
		ans[z] = x;
	}
}
int main()
{
    int n, i, j, x, y;
    cin >> n;
    for(i = 1; i < n; i++)
    {
    	cin >> x >> y;
    	v[x].push_back(y);
    	v[y].push_back(x);
	}
	if(n == 1)	cout << '0';
	else if(n == 2)	cout << '1' << endl << "1 2";
	else
	{
		for(i = 1; i <= n; i++)
		{
			if(v[i].size() >= 2)
			{
				dfs(i,i);
				break;
			}
		}
		cout << (z+1) / 2 << endl;
		for(j = 1; j <= z / 2; j++)
		{
			cout << ans[j] << ' ' << ans[j + (z+1) / 2] << endl;
		}
		if(z&1)	cout << i << ' ' << ans[(z+1) / 2];
	}
	
    return 0;
}
第二场F题

在这里插入图片描述

题意:给出一个n * m的矩阵a,a[i][j] = lcm(i , j),求每个k*k大小的子矩阵的最大值的和

思路:使用单调队列,先横向找每一段长度为k的区间的最大值记录,再在纵向找出同样区间的最大值加起来得到答案

#include<cstdio>
#include<deque>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
int getgcd(int a, int b)
{
	if(b == 0)
		return a;
	else
		return getgcd(b, a % b);
}
int a[5003][5003], b[5003][5003];
deque<int> q;
int main()
{
    ll n, m, k, i, j, ans = 0;
    cin >> n >> m >> k;
    for(i = 1; i <= n; i++)
    {
    	for(j = 1; j <= m; j++)
    	{
    		a[i][j] = i * j / getgcd(i,j);
		}
	}
	for(i = 1; i <= n; i++)
	{
		q.clear();
		q.push_back(0);
		for(j = 1; j < k; j++)
		{
			while(q.size() && a[i][q.front()] <= a[i][j])	q.pop_back();
			q.push_back(j);
		}
		for(j = k; j <= m; j++)
		{
			while(q.size() && q.front() <= j - k)	q.pop_front();
			while(q.size() && a[i][q.back()] <= a[i][j])	q.pop_back();
			q.push_back(j);
			b[i][j] = max(b[i][j],a[i][q.front()]);
		}
	}
	for(j = k; j <= m; j++)
	{
		q.clear();
		q.push_back(0);
		for(i = 1; i < k; i++)
		{
			while(q.size() && b[q.front()][j] <= b[i][j])	q.pop_back();
			q.push_back(i);
		}
		for(i = k; i <= n; i++)
		{
			while(q.size() && q.front() <= i - k)	q.pop_front();
			while(q.size() && b[q.back()][j] <= b[i][j])	q.pop_back();
			q.push_back(i);
			ans += b[q.front()][j];
		}
	}
	cout << ans << endl;
    return 0;
}
第三场B题

在这里插入图片描述

题意:给出一个字符串s和q次操作,操作A x为询问字符串的第x个字符,操作M x为将s的前x个字符移到末端,若x为负则将后|x|个字符移到前端。

思路:将字符串看成一个环,每次M操作只需要改变字符串的头部位置即可,将头部的位置记录下来用于回答A操作。(用cin和cout会超时,难受)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s;
    char order;
    int t, i, l, x, move = 0;
    cin >> s;
    scanf("%d",&t);
    l = s.length();
    while(t--)
    {
    	scanf("%c %d",&order,&x);
    	if(order == 'A')
    	{
    		printf("%c\n",s[(move + x - 1) % l]);
		}
		else
		{
			move = (move + x) % l;
			if(move < 0)
			{
				move = move + l;
			}
		}
	}
    return 0;
}
第三场C题

在这里插入图片描述

题意:给定一个由20个点连接而成的右手掌形状,然后按顺时针或逆时针输入20个点,判断是左手还是右手,输入的手掌只会是给定右手的旋转、翻转,不会放缩。

思路:观察手掌,手掌底部是最长的边,所以首先要找到最长边,然后与手掌底部相连的另外两条长度不同边的位置可以确定手掌的方向

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
double x[25],y[25];
double jl(double x1,double y1,double x2,double y2)
{
	return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
}
int main()
{
	int t, i, p1, p2, l, mid, r;
	scanf("%d",&t);
	while(t--)
	{
		double  len, ma = -1;
		for(i = 1; i <= 20; i++)
		{
			scanf("%lf%lf",&x[i],&y[i]);
		}
		x[0] = x[20], y[0] = y[20];
		x[21] = x[1], y[21] = y[1];
		x[22] = x[2], y[22] = y[2];
		for(i = 1; i <= 20; i++)
		{
			len = jl(x[i],y[i],x[i+1],y[i+1]);
			if(len > ma)
			{
				ma = len;
				p1 = i, p2 = i + 1;
			}
		}
		if(jl(x[p1], y[p1], x[p1-1], y[p1-1]) < jl(x[p2], y[p2], x[p2+1], y[p2+1]))
		{
			l = p1 - 1;
			mid = p1;
			r = p2;
		}
		else
		{
			l = p2 + 1;
			mid = p2;
			r = p1;
		}
		if((x[r]-x[mid])*(y[l]-y[mid])-(x[l]-x[mid])*(y[r]-y[mid])<0)
		{
			printf("left\n");
		}
		else
		{
			printf("right\n");
		}
	}
	return 0;
}



第三场L题

在这里插入图片描述

题意:判断字符串开头是否为lovely

思路:注意case insensitive是不区分大小写的意思

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
string s;
int main()
{
	int i;
	bool p = false;
	cin >> s;
	for(i = 0; i < 6, s[i] != '\0'; i++)
	{
		if(s[i] <= 'Z')	s[i] += 32;
	}
	if(s[0] == 'l')
	if(s[1] == 'o')
	if(s[2] == 'v')
	if(s[3] == 'e')
	if(s[4] == 'l')
	if(s[5] == 'y')
	p = true;
	if(p)	cout << "lovely";
	else	cout << "ugly";
	return 0;
}
第四场B题

在这里插入图片描述

题意:输入t组n和c,求fc(n)的值

思路:进行几次小数字的枚举可知fc(n) = c的(n分解质因数的个数)次幂,利用素数筛列出1e6以内的质数用于分解质因数。

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
ll p[1000006], t[1000006] = {0}, s = 1;
ll qpow(ll x, ll pows)
{
	ll ans = 1;
	while(pows)
	{
		if(pows & 1)	ans = ans * x % mod;
		pows >>= 1;
		x = x * x % mod;
	}
	return ans;
}
void su()
{
	int i, j;
	for(i = 2; i <= 1000000; i++)
	{
		if(t[i] == 0)
		{
			p[s++] = i;
			for(j = i + i; j <= 1000000; j += i)
			{
				t[j] = 1;
			}
		}
	}
}
int main()
{
	su();
	ll n, c, x, i, po, ans;
	scanf("%lld",&n);
	while(n--)
	{
		po = 0;
		scanf("%lld%lld",&x,&c);
		if(x == 1 || c == 1)
		{
			printf("1\n");
		}
		else
		{
			for(i = 1; i < s && p[i] * p[i] <= x; i++)
			{
				while(x % p[i] == 0)
				{
					x /= p[i];
					po++;
				}
			}
			if(x != 1) po++;
			ans = qpow(c,po);
			printf("%lld\n",ans);
		}
	}
}
第四场D题

在这里插入图片描述

题意:给出一个数字字符串,可以将其任意划分成段,求最大的段与最小的段的差值的最小值

思路:如果将长度为n的数字串分成n段,则答案最大为9-0=9,所以有这几种可能的分法:分成n的因数段,每次划分都枚举出差值求差值的最小值;最大段比最小段多一位,相减为个位数,即10…00x和99…99y这样的两段。利用各种函数对字符串进行判断,比较和复制

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
char s[100005];
int a[100005], b[100005];
int p[100005] = {0};
int one(int l, int n)
{
	int i, z = 0, ni = 9;
	for(i = 1; i <= n; )
	{
		if(a[i] == 1)
		{
			if(i + l > n || p[i + l - 1] - p[i])	return 9;
			z = max(z, a[i + l]);
			i += l+1;
		}
		else
		{
			if(i + l - 1 > n || p[i + l - 2] - p[i - 1] != 9 * (l - 1))	return 9;
			ni = min(ni, a[i + l - 1]);
			i += l;
		}
	}
	return 10 - ni + z;
}
bool pd(int *x, int *y, int l)
{
	int i;
	for(i = 1; i <= l; i++)
	{
		if(x[i] != y[i])
		{
			return x[i] < y[i];
		}
	}
	return false;
}
void jian(int *a, int *b, int n)
{
	int i;
	for(i = n; i > 0; i--)
	{
		a[i] -= b[i];
	}
	for(i = n; i > 0; i--)
	{
		if(a[i] < 0)
		{
			a[i-1]--;
			a[i] += 10;
		}
	}
}
int ping(int l, int n)
{
	int i, j;
	int *max_ = a, *min_ = a;
	if(l > 1 && !a[1])	return 9;
	for(i = 0; i < n; i += l)
	{
		if(l > 1 && !a[i+1])	return 9;
		if(pd(a+i,min_,l))	min_ = a + i;
		if(pd(max_,a+i,l))	max_ = a + i;
	}
	memcpy(b,max_,sizeof(int)*(l+10));
	jian(b,min_,l);
	for(i = 1; i < l; i++)
	{
		if(b[i])	return 9;
	}
	return b[l];
}
int main()
{
	int t, ans, n, i;
	scanf("%d",&t);
	while(t--)
	{
		ans = 9;
		scanf("%d%s",&n,s+1);
		for(i = 1; i <= n; i++)
		{
			a[i] = s[i] - '0';
			p[i] = p[i-1] + a[i];
		}
		for(i = 1; i <= n/2; i++)
		{
			ans = min(ans, one(i,n));
			if(n % i == 0)
			{
				ans = min(ans, ping(i,n));
			}
		}
		cout << ans << endl;
	}
	return 0;
}
第四场F题

在这里插入图片描述

题意:已知AB//CD,输入t组数据ac,ad,bc,bd,求向量AB和向量CD的方向是否相反

思路:
在这里插入图片描述
因为三角形两边之和大于第三边,所以AO+OC>AC,BO+OD>BD => AD + BC > AC + BD,把C和D反过来也是一样,所以判断(AD + BC) 与 (AC + BD)的大小即可得到答案。
(比赛时题目没读清白导致不会做)

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int main()
{
	int t, ac, ad, bc, bd;
	cin >>t;
	while(t--)
	{
		cin >> ac >> ad >> bc >> bd;
		if(ac + bd < ad + bc)	cout << "AB//CD" << endl;
		else	cout << "AB//DC" << endl; 
	}
	return 0;
}
第五场F题

在这里插入图片描述

题意:根据输入的数字画出dps的直方图,dps最高的用 * 标记出来
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
int d[102], s[102];
int main()
{
	int n, max_ = 0, i, j;
	cin>>n;
	for(i = 0; i < n; i++)
	{
		cin >> d[i];
		max_ = max(d[i],max_);
	}
	for(i = 0; i < n; i++)
	{
		s[i] = 50ll * d[i] / max_;
		if(50ll * d[i] % max_ != 0) s[i]++;
		cout<<'+';
		for(j = 0; j < s[i]; j++) cout << '-';
		cout<<'+'<< endl << '|';
		for(j = 0; j < s[i] - 1; j++) cout << ' ';
		if(s[i] != 0)
			cout << (d[i] == max_ ?'*':' ');
		cout << '|' << d[i] << endl << '+';
		for(j = 0; j < s[i]; j++) cout << '-';
		cout << '+' << endl;
	}
	return 0;
}
第五场I题

在这里插入图片描述

题意:一个矩阵由H、E、G组成,要求每个H都至少有一个E和G相邻,求矩阵无限大时H占的最大比例

思路:因为H要占最大比例,所以每个E和G的四周都必须是H
在这里插入图片描述
照这样画一下矩阵即可。

#include<cstdio>
int main()
{
	printf("0.666667");
	return 0;
}
第六场B题

在这里插入图片描述

题意:给出一个n阶方阵,元素只为1或0,求矩阵的秩为n的概率

思路:找规律,满足条件时:
对于第一行来说,有2n -1种情况(不能全为0),
对于第二行来说,有2n -2种情况(不能全为0,不能被第一行表示),
对于第三行来说,有2n -4种情况(不能全为0,不能被一二行表示),
……
所以对第i行来说,有2n – 2i–1 种情况.
所以秩为n的概率为
在这里插入图片描述

#include <cstdio>
typedef long long ll;
const ll N = 2e7, mod = 1e9+7;
ll t, n, f[N], inv[N], ans[N], i, v;
ll qpow(ll x, ll pows)
{
	ll ans = 1;
	while(pows)
	{
		if(pows & 1)	ans = ans * x % mod;
		pows >>= 1;
		x = x * x % mod;
	}
	return ans;
}
int main()
{
	f[0] = 1;
	for(i = 1; i <= N; i++)
	{
		f[i] = 2 * f[i-1] % mod;
	}
	inv[N] = qpow(f[N],mod-2);
	for(i = N-1; i > 0; i--)
	{
		inv[i] = 2 * inv[i+1] % mod;
	}
	v = ans[1] = inv[1];
	for(i = 2; i <= N; i++)
	{
		v = v * (f[i] - 1) % mod * inv[i] % mod;
		ans[i] = ans[i-1] ^ v;
	}
	scanf("%lld",&t);
	while(t--)
	{
		scanf("%lld",&n);
		printf("%lld\n",ans[n]);
	}
	return 0;
}
第六场C题

在这里插入图片描述
在这里插入图片描述

题意:定义一个矩阵的压强P等于矩阵里所有值的和F除以矩阵最下方一行的和S,给出t个矩阵,求每个矩阵的子矩阵的最大压强。

思路:设有两个矩阵且F1/S1 > F2/S2,那么一定有F1/S1 > (F1+F2)/(S1+S2),(想象成两杯浓度不同的盐水混合),所以压强最大的子矩阵一定列数为1,因此只需要用前缀和把每个列数为1的子矩阵F记下,再遍历它们求出S的最大值。
(用cin 、cout超时了)

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
double sum[202][202] = {0}, z[202][202];
int main()
{
	int t, n, m, i, j;
	double ans, max_;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(i = 1; i <= n; i++)
		{
			for(j = 1; j <= m; j++)
			{
				scanf("%d",&z[i][j]);
				sum[i][j] = sum[i-1][j] + z[i][j];
			}
		}
		ans = 0;
		for(i = 1; i <= n; i++)
		{
			max_ = 0;
			for(j = 1; j <= m; j++)
			{
				max_ = max(max_, sum[i][j] / z[i][j]);
			}
			ans = max(max_, ans);
		}
		printf("%.8lf\n",ans);
	}
	return 0;
}
第六场E题

在这里插入图片描述

题意:输入n和k,要求输出一个1到n组成的序列P,满足对于任意的i(1<=i<=n),存在长度为i的区间,此区间的和对n取余为k

思路:当i等于n时区间唯一,所以k应该为定值,n为奇数时k为0,n为偶数时k为n/2

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
int ans[5003];
int main()
{
	int i, j, n, k;
	cin >> n >> k;
	if(n*(n+1)/2%n == k)
	{
		if(n&1)
		{
			ans[1] = n;
			for(i = 3, j = 1; i <= n; i += 2, j++)
			{
				ans[i - 1] = j;
				ans[i] = n - j;
			}
		}
		else
		{
			ans[1] = n, ans[2] = n / 2;
			for(i = 4, j = 1; i <= n; i += 2, j++)
			{
				ans[i - 1] = j;
				ans[i] = n - j;
			}
		}
		for(i = 1; i <= n; i++)
		{
			cout << ans[i] << ' ';
		}
	}
	else
	{
		cout << "-1";
	}
	return 0;
}
第七场B题

在这里插入图片描述

题意:有n*m个口罩,要将口罩装入盒子,分给两种医院,a医院n座,b医院m座满足装盒的方案能均分给两种医院,要求盒子数尽量少,并且分装方案的字典序尽量大

思路:当n<=m时,每个盒子最多n个口罩,否则无法均分给m个医院,所以先每个a医院分n个口罩,
然后对于a类医院(m-n)个医院每个分n个口罩,对于b类医院n个医院每分(m-n)个口罩…以此类推类似gcd的递归过程

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
ll ans[100005], a;
void gcd(ll n, ll m)
{
	if(!m)	return;
	int i;
	for(i = 0; i < (n/m)*m; i++)
	{
		ans[a++] = m;
	}
	gcd(max((n%m),m),min((n%m),m));
}
int main()
{
	ll i, t, n, m;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld",&n,&m);
		if(n < m)	swap(n,m);
		a = 1;
		gcd(n,m);
		printf("%lld\n",a-1);
		for(i = 1; i < a; i++)
		{
			printf("%lld ",ans[i]);
		}
		printf("\n");
	}
	return 0;
}
第七场D题

在这里插入图片描述

题意:对每组输入n判断

是否为完全平方数在这里插入图片描述
思路:很容易想到这个公式
在这里插入图片描述
我的思路:对n、(n+1)、(2n+1)分解质因数,除去6,判断每个质因数的个数是否为偶数
大佬的思路与证明:https://www.zhihu.com/question/363661682证明只需要特判n是否为1或24
经过队友的尝试发现可以直接开方验证,数据范围并没有题目里描述的那么大

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
int main()
{
	ll t, n, a;
	double b, c;
	scanf("%lld",&t);
	while(t--)
	{
		scanf("%lld",&n);
		ll a = sqrt(n * (n+1) * (2*n+1) / 6);
		b = sqrt(n * (n+1) * (2*n+1) / 6);
		c = b;
		if(a == c)
			printf("Fake news!\n");
		else
			printf("Nobody knows it better than me!\n");
	}
	return 0;
}
第七场H题

在这里插入图片描述

题意:(1,k)一定是传奇组, 如果(n,k)是传奇组,那么(n+k,k)和(nk,k)也是传奇组。

输入n和k的最大值,求有多少个传奇组(对1e9+7取模)。
思路:由规则可知如果(n,k)是传奇组,设正整数x,那么n一定是xk或者xk+1或者1

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
int main()
{
	ll n, k, i, j, ans = 0;
	cin >> n >> k;
	for(i = 2; i <= n && i <= k; i = j+1)
	{
		j = min(n/(n/i),k);
		ans = (ans + (j - i + 1) % mod * (n/i) % mod) % mod;
	}
	n = n-1;
	for(i = 2; i <= n && i <= k; i = j+1)
	{
		j = min(n/(n/i),k);
		ans = (ans + (j - i + 1) % mod * (n/i) % mod) % mod;
	}
	ans = (ans + n + k) % mod;
	cout << ans;
	return 0;
}
第十场A题

在这里插入图片描述

题意:t组输入,每组给出一个素数p,问能否使1至p-1组成一个排列x,满足在模p下x[i-1]与2x[i]或者3x[i]同余,能则输出此排列,不能则输出-1.

思路:尝试从x[1]=1开始,i>1,x[i-1]一定是小于p的,所以题目要求实际上是2*x[i]%p或者3*x[i]%p等于x[i-1]。依此规律规律按先尝试2倍再3倍往下尝试即可。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
typedef long long ll;
int ans[1000006] = {0,1};
int v[1000006] = {0,1};
int main()
{
	int t, p, i, k;
	bool q;
	scanf("%d",&t);
	while(t--)
	{
		for(i = 2; i < p; i++)
		{
			v[i] = 0;
		}
		q = true;
		scanf("%d",&p);
		for(i = 2; i < p; i++)
		{
			k = 2 * ans[i-1] % p;
			if(v[k] == 0 && k != 0)
			{
				ans[i] = k;
				v[k] = 1;
			}
			else
			{
				k = 3 * ans[i-1] % p;
				if(v[k] == 0 && k != 0)
				{
					ans[i] = k;
					v[k] = 1;
				}
				else
				{
					q = false;
					break;
				}
			}
		}
		if(q)
		{
			for(i = 1; i < p; i++)
			{
				printf("%d ",ans[i]);
			}
		}
		else
		{
			printf("-1");
		}
		printf("\n");
	}
	return 0;
}
第十场D题

题面太长不截图了

题意:炉石传说游戏:有四种鱼人,它们都带有剧毒,身材为1e9/1e9,剧毒可以杀死任何受到它们伤害的随从,这四种鱼人分别是:圣盾和亡语;只有圣盾;只有亡语;没有圣盾和亡语。圣盾在抵消一次伤害后消失,亡语则是在随从死亡时生成一个身材1/1的无效果plant随从。随从的身材为x/y的形式,代表攻击/血量,当随从互相攻击时我攻击者会扣除对方攻击数值的血量,同时对方也会扣除攻击者攻击数值的血量,血量小于等于0则随从死亡。随从数先为0的一方失败。现在给出你和对手的四种随从的数量,问能否有胜利的可能。

思路:正常游戏里随从是随机攻击,但这题只是问能否胜利,所以我们只需要安排对我方最有利的攻击方式后再判断即可,思路写在代码注释里

#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
typedef long long ll;
int main()
{
	int t, a1, a2, a3, a4, a5, b1, b2, b3, b4, b5;
	//1.圣盾亡语 2.圣盾 3.亡语 4。白板 5.1/1的plant 
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d%d%d%d%d%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4);
		a5 = 0;
		b5 = 0;
		while((a1+a2+a3+a4) && (b1+b2+b3+b4))
		{
			if(b5 && (a3 || a4))//对手的1/1全部白给 
			{
				b5 = 0;
			}
			if(a5 && (b1 + b2))//用我方1/1破除对方圣盾鱼人 
			{
				if(b1)
				{
					b1--;
					b3++;
					a5--;
				}
				else
				{
					b2--;
					b4++;
					a5--;
				}
			}
			if(a3)//优先使用不带圣盾的亡语 
			{
				a3--;
				a5++;
			}
			else
			{
				if(a1)//优先使用我方圣盾进攻 
				{
					a1--;
					a3++;
				}
				else if(a2)//我方无圣盾时先打出亡语 
				{
					a2--;
					a4++;
				}
				else
				{
					a4--;
				}
			}
			if(b3)//优先杀死对方的无圣盾亡语出1/1白给 
			{
				b3--;
				b5++;
			}
			else
			{
				if(b4)//优先使对方减员 
				{
					b4--;
				}
				else if(b1)//破除对方的亡语圣盾 
				{
					b1--;
					b3++;
				}
				else
				{
					b2--;
					b4++;
				}
			}
		}
		if(b1+b2+b3+b4 || !(a1+a2+a3+a4) && a5<=b5)
		{
			printf("No\n");
		}
        else
        {
        	printf("Yes\n");
		}
	}
	return 0;
}
第十场E题

在这里插入图片描述
在这里插入图片描述

题意:给出t组这样的一摞方块,可以把方块向左边推动使其落下,问最终这摞方块的高度的最小值为多少

思路:向左推动实际上是在平均分配推动列以左的方块,答案为第i列的前缀和除以i得到的最大值。

#include<cstdio>
typedef long long ll;
ll k[100005] = {0};
int main()
{
	ll t, i, m, n, x, ans;
	scanf("%lld",&t);
	while(t--)
	{
		ans = 1;
		scanf("%lld",&n);
		for(i = 1; i <= n; i++)
		{
			scanf("%lld",&x);
			k[i] = k[i-1] + x;
			if(k[i] % i != 0)
			{
				m = k[i] / i + 1;
			}
			else
			{
				m = k[i] / i;
			}
			if(m > ans)	ans = m;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值