SCU补题20241215

SCU 20241215

E:

题目大意:给定长度为 \(n\) 的数列,问其中多少连续子段是等差数列

#include <iostream>
#define streampreset() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;

int n;
int a[100010];
int b[100010];

int main()
{
    long long ans = 0;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i < n; i++) b[i] = a[i + 1] - a[i];
    long long i = 1;
    while (i < n) {
        long long j = i + 1;
        while (j < n && b[j] == b[i]) j++;
        ans += (j - i + 1) * (j - i) / 2;
        i = j;
    }
    cout << ans + n << endl;
    return 0;
}

双指针,维护差分数组b,当j指向的差和i指向的差相同时,将 j 后移一位,直到移到n-1位(即差分数组的最后一项)或移到差分值不同的元素上

然后计算区间对答案的贡献(j - i + 1) * (j - i) / 2,这里计算的子序列至少要有两个元素

贡献公式的数学推导如下,假设一个含有 \(n\) 个元素的序列 \(1,2,3 . ..n-1,n\)

包含 \(1\) 和后面的元素组成一共 \(n-1\) 个连续的子序列
包含 \(2\) 和后面的元素组成一共 \(n-1-1=n-2\) 个连续的子序列

......

包含 \(n-1\) 和后面的元素组成一共 \(n-n+1=1\) 个连续的子序列

对所有的子序列求和有 \(\sum_{i=1}^{n-1}n-i=\frac{(n-1)*n}{2}\) 其中,\(n\) 为元素个数,对应我们的j - i + 1

G:

题目大意:给定一个整数 \(n\),回答从 \([1,n]\) 中最多选出多少数两两互质

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

bool p[1000010];
vector<int> mem; 

void pre(void) {
    for (int i = 2; i * i <= 1000010; i++)
        if (!p[i])
            for (int j = i * i; j <= 1000010; j += i)
                p[j] = 1; 
    for (int i = 2; i < 1000010; i++)
        if (!p[i])
            mem.push_back(i);
}

int main()
{
    int t;
    cin >> t;
    pre(); 
    while (t--) {
        int n;
        cin >> n;
        int ans = upper_bound(mem.begin(), mem.end(), n) - mem.begin();
        cout << ans + 1 << endl; 
    }
    return 0;
}

打表加二分,首先预处理出题目范围内的所有素数 (\(n\le 10^6\)

使用埃拉托斯特尼筛,原理是从 \(2\) 开始筛除,每次将 \(i\) 的所有在范围内倍数都标记为筛去

最后将范围内没有被筛出的数装进 vector

给定 \(n\) 后,二分查找 vector 内第一个大于等于 \(n\) 的质数的下标,最后计算下标差得到区间内的素数个数

最后答案加上一输出,因为 \(1\) 与任何数都互质

K:

题目大意:有两个箱子,每个时刻向其中分别放入 \(a_i,b_i\) 个物品,每个时刻可以使其中一个箱子的容量 \(+1\) 求箱子最小初始容量

#include <iostream>
using namespace std;

int a[10010], b[10010];
int n;

bool judge(int x) {
	int u = x, v = x, sum = 0;
	for (int i = 0; i < n; i++) {
		sum++;
		if (u >= a[i]) u -= a[i];
		else {
			if (u + sum < a[i]) return 1;
			sum -= a[i] - u;
			u = 0;
		}
		if (v >= b[i]) v -= b[i];
		else {
			if (v + sum < b[i]) return 1;
			sum -= b[i] - v;
			v = 0;
		}
	}
	return 0;
}

int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> n;
		for (int i = 0; i < n; i++) cin >> a[i] >> b[i];
		int l = 0, r = 1e6;
		while (l+1!=r)
		{
			int mid = l + r >> 1;
			if (judge(mid)) l = mid;
			else r = mid;
		}
		cout << r << endl;
	}
	return 0;
}

二分找解,再判断解的合理性,模拟流水线工作

每个时刻可以让其中一个箱子容量 \(+1\) ,可以转化成有一个通用箱子,这个箱子一开始容量为 \(0\) ,每个时刻容量固定 \(+1\)

如果某个箱子不够放,可以将多余的求放入通用箱子内

每个时刻固定给一个箱子增加的容量与全局的通用箱子的容量是等价的

  • 每个时刻可以增加的容量是累积的
  • 这个累积的容量可以动态地分配给每个箱子

对于两个箱子而言,每个时刻需要增加的容量为

\[Extend_u=max(0,a_i-u) \\ Extend_v=max(0,b_i-v) \]

某时刻需要增加的容量转化为以前的时刻可以累加的容量,从全局的通用箱子内借走

H:

题目描述:给定 \(n\) ,在 \([1,n]\) 中找一段最大区间,使得其中的元素两两互不整除

#include <iostream>
using namespace std;

int n;

int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> n;
		cout << (n + 1) / 2 << endl;
		for (int i = n / 2 + 1; i <= n; i++) cout << i << ' ';
		cout << endl;
	}
	return 0;
}

数学题

设有两个数 \(a,b\)\(a>b\) )若它们能够相互整除,则有

\[a=n\times b\quad n\in N^+, n>1 \]

可以发现,\(n\) 最小可取 \(2\) ,则该区间内的最大元素不能是最小元素的两倍,也就是区间 \([l,r],r<2\times l\)

由于最大元素可以取到 \(n\) ,则最小元素就能取到 \(\frac{n}{2}\),取整后得到最大区间为

\[[\lfloor\frac{n}{2}\rfloor+1,n] \]

区间内最多有 \(\lceil\frac{n}{2}\rceil\) 个元素,也可以科学归纳法得出普遍规律

posted @ 2024-12-29 15:23  才瓯  阅读(10)  评论(0)    收藏  举报