【2020-10-1 模拟赛】

今年悲催的在外过国庆 + 中秋
100 + 82 + 70 + 2 = 257
全场Rk8(我太菜辽)

A

回家 (home)
1.1 题目描述
一只袋鼠在数轴上。0 时刻,袋鼠位于 0 处。在时刻 i−1 和 i 之间的时间段中,
袋鼠要么待在当前位置,要么向左或向右跳恰好 i 单位长度。也就是说,如果时
刻 i − 1 时它在 x 位置,那么时刻 i 时它可能的位置是 x − i、x 或 x + i。袋鼠
的家在 X 位置处,它想尽快从 0 走到 X。求袋鼠最早在哪个时刻可以到达 X。
1.2 输入格式
一行一个整数 X。
1.3 输出格式
一个整数,表示袋鼠最早在哪个时刻可以到达 X。

算法

签到题
这题等价于把1~n这个区间分成k个子区间
其中(k_1 < k_2 < k_3 < k_4······<k_i)
k_i就是我们的答案
比赛的时候用的二分 检验一个mid 如果(1 + 2 + 3 + ······ + mid < n)

代码

#include<iostream>
#include<cstdio>
#include<map>
#define ll long long
using namespace std;
ll n;
ll l,r;
bool cheek(ll num)
{
    ll ans = 0;
    for(ll i = num;i >= 1;i--)
    {
    	ans += i;
    	if(ans >= n)
    	return true;
	}
	return false;
}
int main()
{
	freopen("home.in","r",stdin);
	freopen("home.out","w",stdout);
	scanf("%lld",&n);
	l = 1,r = n;
	while(l <= r)
	{
		ll mid = (l + r)>>1;
		if(cheek(mid))
		r = mid;
		else
		l = mid + 1;
		if(l == r)
		break;
	}
	cout<<r;
}

B

不必要 不必要 (unnecessary)
2.1 题目描述
小 A 有 N 张写有正整数的牌,第 i 张牌上的数是 a i (1 ≤ i ≤ N) 。因为他很
喜欢大的数,所以他认为一个卡牌的子集是“好”的,当且仅当该子集中的卡
牌上的数之和大于等于 K(卡牌上的数可以相同) 。
接下来,他会判断每张牌 i 是否是“不必要”的,判断方法如下:
• 如果所有包含 i 的“好”的子集去掉 i 后仍然是“好”的,那么 i 是“不
必要”的。
• 否则,i 不是“不必要”的。
请你求出“不必要”的牌的张数。
注意:每张牌的判断是独立的,并且他不会扔掉那些被判断为“不必要”的牌。
2.2 输入格式
第一行,两个正整数 N,K。
第二行,N 个正整数 a i 。
2.3 输出格式
一个数, “不必要”的牌的张数。

心路历程

刚开始看到这道题
大概10min码完了
然后因为题目样例给的太水
我的错误想法水过了全部的样例
我的想法是先把所有元素排序 然后对最后这个数组进行子序列的算法 让每个子序列(a_i < a_{i+1} < a_{i+2} < ······ < a_j) 其中

(left{ egin{array}{**lr**} a_{i} + a_{i+1} + a_{i+2} + a_{i+3} ······ + a_j geq k\ a_{i+1} + a_{i+2} + a_{i+3} ······ + a_j < k end{array} ight.)

维护这些子序列的并的元素的数量为cnt
最后输出 n - cnt
这样明显是错的
没有考虑到所有的子集
这里提供一组bug数据
stdin:
5 13
2 2 2 2 9
stdout:
0
但是这个错误程序跑了82分 真怀疑数据用脚造的
最后十分钟发现了这个错误但还是无力回天了
难过

算法

暴力的算法就不考虑了

算法一:

我们记(f_i[s],g_i[s])分别表示(1~i,i~N)是否存在和为s的子集
然后枚举每张牌(i),用(f_{i - 1} 、g_{i + 1})判断是否存在和为([K-a_i,K))的子集。
如果存在 那么这张牌(i)是必要的
我们最后减去所有必要的牌就是答案
时间复杂度: (O(NK))

算法二:

我们考虑优化算法一:
对于(a_igeq a_j) 如果(a_j)是不必要的 那么(a_i)也是不必要的
将a从小到大排序 那么“不必要”的一定是一段前缀和
我们从后往前加入每个元素 我们考虑我们现在加入到第(i + 1 ~ N) 此时 考虑 有(p_{i + 1} 个不必要的牌)
考虑加入(i)如何计算(p_i)
先考虑i是否是只考虑(i ~ N)时不必要的牌
我们只需要判断(i + 1 ~ N)(< K)的子集和的最大值是否大于(geq K - a_i)
(s_i)表示(i ~ N)(< K)的子集和的最大值

(s_i = left{ egin{array}{**lr**} s_{i + 1} (if a_i + s_{i +1}geq K)\ a_i + s_{i + 1} (if a_i + s_{i + 1}< K) end{array} ight.)
(s_i)可将只考虑(i ~ N)(i)不是"不必要"的条件改写成
(s_{i + 1}geq K - a_i)也即 (a_i + s_{i + 1}geq K)
下面分情况讨论:

  • 如果(a_i + s_{i + 1}geq K) 那么在只考虑(i ~ N)(i)不是不必要的 所以(p_i = 0)
  • 如果(a_i + s_{i + 1} < K) 那么 (p_i = p_{i + 1} + 1)
    最后答案为 (p_1)
    时间复杂度:(O(NlogN))

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n,k,a[1000000],s,p;
bool cmp(ll x,ll y)
{
	return x > y;
}
int main()
{
	freopen("unnecessary.in","r",stdin);
	freopen("unnecessary.out","w",stdout);
	scanf("%lld%lld",&n,&k);
	for(ll i = 1;i <= n;i++)
	scanf("%lld",&a[i]);
	sort(a + 1,a + n + 1,cmp);
	for(ll i = 1;i <= n;i++)
	{
		if(s + a[i] >= k)
		p = 0;
		else
		p += 1,s = s + a[i];
	}
	cout<<p;
}

C

大意:给定了一个数列 (A_1,A_2,······,A_n)
一个区间的权值为:
(W(L,R)=(R−L+1)×gcd(A_l,...,A_r))
(W_max)

考场上

打了个暴力 加了个剪枝
水到了70

**#include<iostream>
#include<cstdio>
#define ll long long 
using namespace std;
ll gcd(ll a,ll b)
{
	return a == 0?b:gcd(b%a,a);
}
ll read()
{
	ll f = 1,ans = 0;
	char a;
	a = getchar();
	while(a != '-' && (a >'9'||a <'0'))
	a = getchar();
	if(a == '-')f = -1,a = getchar();
	while(a <= '9' && a >= '0')
	ans = (ans << 3) + (ans << 1) + (a - '0'),a = getchar();
	return ans * f;
}
ll n,a[100005],ans;
int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);   
    n = read();
    for(ll i = 1;i <= n;i++)
    a[i] = read();
    for(ll l = 1;l <= n;l++)
    {
    	ll g = a[l];
    	for(ll r = l;r <= n;r++)
        {
        	g = gcd(g,a[r]);
        	ans = max(ans,g * (r - l + 1));
        	if(g * (n - l + 1) <= ans)//利用gcd向右扩张只会减少不会增加
        	break;        	
		}
	}
	cout<<ans;
}

正解:

有分治的做法 但是这里用(O(nlog^2n))的方法
我们对于一个r 我们知道左边的(gcd(a[l]...a[r])gcd(a[l]...a[r]))一定小于等于(gcd(a[l+1]...a[r])gcd(a[l+1]...a[r]))
同时对于一个数m 如果他和别的数的(gcd)不等于m 则必定小于(m/2) 所以最多对于一个区间有(logn)(gcd)
所以我们枚举r,用一个队列存储这log个不同的gcd的左端点,每次直接枚举这些gcd更新,更新的时候加一些判断处理重复就好了,写起来很容易。

代码

#include<iostream>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
ll n;
queue<int> QAQ;
queue<int> lins;
ll g[100005],ans;
ll gcd(ll x,ll y)
{return (x == 0)? y : gcd(y % x,x);}
int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);	
	scanf("%lld",&n);
	g[0] = -1;
	for(ll i = 1;i <= n;i++)
	{
		scanf("%lld",&g[i]);//输入
		int last = 0;//上一个gcd段的最后的一位
		while(!QAQ.empty()) 
		{
		    ll  x = QAQ.front();	
		    QAQ.pop();
		    g[x] = gcd(g[x],g[i]);
		    ans = max(ans,g[x] * (i - x + 1));//计算权值 
		    if(g[x] == g[last])continue;//如果和上一段一样则不必存
			lins.push(x);
			last = x; 
		}
		ans = max(ans,g[i]);
		while(!lins.empty())
		{
			QAQ.push(lins.front());
			lins.pop();
		} 
		if(g[last] != g[i]) QAQ.push(i);
	}
	printf("%lld
",ans);
	return 0;
}

D

4.1 问题描述
沙都子热衷于制造陷阱。她制造了一个 (n × n) 的陷阱,其中 (n) 为奇数 奇数。第 i 行
第 j 列的区域的伤害值为 (a_{i,j}),如果 (a_{i,j} < 0) 则说明这一区域会帮其回血。
但沙都子对这个陷阱不太满意,打算对其进行改造。沙都子太懒,不想一个格
子一个格子改造。令 (m = (n + 1)/2),每一次沙都子会将一个 (m × m)的连续子
矩形的伤害值都乘上 (−1)。沙都子想知道,经过若干次改造后,陷阱所有区域的
伤害值之和最大是多少呢?
4.2 任务描述
4.2.1 输入
第一行,一个正整数 n,保证 n 为奇数。
接下来 n 行,每行 n 个整数 a i,j ,表示初始伤害值。
4.2.2 输出
一行,表示进行若干次改造后,陷阱所有区域的伤害值之和的最大值。

考场上:

被样例误导了 以为这个(m x m)的矩阵只会在四周
然后码了很久
最后5分。。。
要注意看题

正解(采用STD)

代码

#include <cstdio>
#include <algorithm>
#define N_MAX 35
#define M_MAX ((N_MAX + 1) / 2)
const int INF = 0x3F3F3F3F;
inline int f(int t, int v) { return t ? -v : v; }
int n, m, i, j, s, t, a[N_MAX + 1][N_MAX + 1], sum, tmp, cur, ans = -INF;

#define FIO "trap"
int main()
{
	freopen(FIO ".in", "r", stdin);
	freopen(FIO ".out", "w", stdout);

	scanf("%d", &n);
	for (i = 1; i <= n; ++i)
		for (j = 1; j <= n; ++j)
			scanf("%d", &a[i][j]);
	m = n + 1 >> 1;
	for (t = 0; t < 1 << m; ++t)
	{
		cur = f(t & 1, a[m][m]);
		for (j = 1; j < m; ++j)
			cur += f(t >> j & 1, a[m][j]);
		for (j = 1; j < m; ++j)
			cur += f(t & 1 ^ t >> j & 1, a[m][j + m]);
		for (i = 1; i < m; ++i)
		{
			tmp = -INF;
			for (s = 0; s < 2; ++s)
			{
				sum = f(s, a[i][m]) + f(s ^ t & 1, a[i + m][m]);
				for (j = 1; j < m; ++j)
					sum += std::abs(a[i][j] + f(s, a[i][j + m]) + f(t >> j & 1, a[i + m][j]) + f(s ^ t & 1 ^ t >> j & 1, a[i + m][j + m]));
				tmp = std::max(tmp, sum);
			}
			cur += tmp;
		}
		ans = std::max(ans, cur);
	}
	printf("%d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/dixiao/p/13759086.html