首页 > 其他分享 >AGC008

AGC008

时间:2025-01-18 14:21:28浏览次数:1  
标签:空位 le cout int s1 AGC008 正数

AGC008

B

题目大意

给出一个序列,一开始全是白色,一次操作可以染黑或染白一段长度为 \(K\) 的区间,要让最后序列中黑色格子上数的和最大,求这个最大值。

解题思路

考虑找结论

发现我们一定要尽可能地把正数涂黑,负数涂白,由于对操作次数没有限制,因此对一个正数我们只要将其放在区间首位涂黑,再把后面的数涂白即可实现单独将其涂黑。

但是我们发现如果这么做最后的 \(K\) 个数就无法实现,同理,如果逆转从后往前涂那么前 \(K\) 个数就无法实现。

考虑一段从前往后,一段从后往前,中间就会有 \(K\) 个数无法实现,而其他正数都能取到

因此我们可以枚举这个无法实现的区间,如果它求和大于 \(0\) 就加上,否则不加,然后将剩下的所有正数求和加上。

代码上,我们可以维护两个前缀和,一个是序列的前缀和 \(s_1\),一个是正数的前缀和 \(s_2\)。

答案即为:

\[\max_{i=1}^{n-k+1}{(s_2[i - 1] + s_2[n] - s_2[i + k - 1] + \max(0, s_1[i + k - 1] - s_1[i - 1]))} \]

代码

#include<bits/stdc++.h>
#define endl "\n"
#define ll long long
using namespace std;

const int N = 1e5 + 10;

int n, k;
ll s1[N], s2[N], ans;

int main()
{
	ios :: sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
	{
		cin >> s1[i];
		s2[i] = s2[i - 1];
		if (s1[i] > 0)
		{
			s2[i] += s1[i];
		}
		s1[i] += s1[i - 1];
	}
	for (int i = 1; i <= n - k + 1; i++)
	{
		ans = max(ans, s2[i - 1] + s2[n] - s2[i + k - 1] + max(0ll, s1[i + k - 1] - s1[i - 1]));
	}
	cout << ans << endl;
	return 0;
}

D

题目大意

构造一个序列 \(a\),使得:

  1. \(a\) 的长度为 \(N^2\),并且满足数字 \(1,2, \cdots, N\) 都各出现恰好 \(N\) 次。

  2. 对于 \(1 \le i \le N\),数字 \(i\) 在 \(a\) 中第 \(i\) 次出现的位置是 \(X_i\)。

解题思路

考虑贪心

题意即为:对于 \(1 \le i \le N\),位置 \(X_i\) 上的数为 \(i\),前面有 \(i-1\) 个 \(i\),后面有 \(n -i\) 个。

考虑先满足前一个条件,我们可以对 \(X_i\) 从小到大排序,然后放在 \(X_i\) 前面空位即可,放不下就必定无解。

但是我们为了顾及后面一个条件,那么就需要尽可能少地占用后面的空位,因此我们填数时在最前面的空位一定是最优的。

于是后面一个条件只需从大到小遍历,找后面的空位放即可(不一定要选取最后面,但为了编写方便,不妨就选最后的空位)。

代码

#include<bits/stdc++.h>
#define endl "\n"
using namespace std;

const int N = 510;

struct node
{
	int id, x;
} b[N];

int n;
int a[N * N];

bool cmp(node x, node y)
{
	return x.x < y.x;
}

int main()
{
	ios :: sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> b[i].x;
		a[b[i].x] = i;
		b[i].id = i;
	}
	sort(b + 1, b + n + 1, cmp);
	for (int i = 1, p = 1; i <= n; i++)
	{
		for (int j = 1; j < b[i].id; j++)
		{
			while (p <= b[i].x && a[p] != 0)
			{
				p++;
			}
			if (p > b[i].x)
			{
				cout << "No" << endl;
				return 0;
			}
			a[p++] = b[i].id;
		}
	}
	for (int i = n, p = n * n; i >= 1; i--)
	{
		for (int j = b[i].id + 1; j <= n; j++)
		{
			while (p >= b[i].x && a[p] != 0)
			{
				p--;
			}
			if (p < b[i].x)
			{
				cout << "No" << endl;
				return 0;
			}
			a[p--] = b[i].id;
		}
	}
	cout << "Yes" << endl;
	for (int i = 1; i <= n * n; i++)
	{
		cout << a[i] << " ";
	}
	return 0;
}

标签:空位,le,cout,int,s1,AGC008,正数
From: https://www.cnblogs.com/lintianchen/p/18678431

相关文章

  • AGC008 题解
    A简要题意:花费1代价+1或取反,求把\(x\)变成\(y\)的最小代价显然的,取反最多只会用两次,且必在头尾,那么直接枚举就完了代码:#include<bits/stdc++.h>#defineintlonglong#definerep(i,l,r)for(inti=l;i<=r;i++)#defineper(i,l,r)for(inti=l;i>......
  • [AGC008F] Black Radius
    缕一缕大概思路。首先假设所有点都被喜欢。一个集合可能被多个\((u,d)\)表示出来,我们取最小\(d\),只有在全集的时候一个最小\(d\)可能会有多个\(u\)进行对应,所以我们去掉对全集的统计。接着考虑以一个点为中心的充要条件。设为\((u,d)\),首先\(d\)不能太大,否则会超出......
  • 【题解】AGC008F | 思维 统计技巧 换根 二次扫描
    题意:给出一个\(n\)个点的树(边权为\(1\))和集合\(S\),求有多少个点集\(T\)可以被表示为离\(S\)中的一个点\(u\)距离不超过\(d\)的点构成的集合(下文称为\(u\)的\(d\)级邻域)。考虑\(S\)为所有点的特殊情况:我们直接求每个点邻域的个数再求和,会算重一些点集,这种情况......
  • AGC008E Next or Nextnext 解题报告
    \(\text{分析}\)\(i\toa_i\)构成内向基环树,配合暴力程序观察内向基环树常见的一些特殊情况:灰色笔对应的是\(i\toa_i\),黑色笔对应的是\(i\top_i\),我们相当于要构造一个黑色的排列(若干环)使得每一条灰色边的起点可以通过一条或两条黑色边到达终点。\(a_i=i\)(全是自环):可以任......
  • AGC008C Tetromino Tiling
    需要注意细节的图形趣题。给出如下图的\(7\)种俄罗斯方块各\(a,b,c,d,e,f,g\)块,可以旋转不能翻转,要求拼成宽度为\(2\)的长方形。输出能得到的最大长度的一半。不难发现,第\(3,6,7\)种方块压根用不上,因为它们造成了长度为\(1\)的凹槽,而这些凹槽永远不可能被填平:要填平......
  • [AGC008F] Black Radius
    记\(S(u,d)\)表示与\(u\)的距离不大于\(d\)的点构成的点集。为了方便后面的讨论,先加入全集的贡献\(1\)。当所有点均可选时,考虑如何不重的计算点集,有些题解写的是......