19级暑假第四场训练赛

A题:1165A

19级暑假第四场训练赛_第1张图片

Input

11 5 2
11010100101

Output

1

Input

11 5 1
11010100101

Output

3

因为只关注对\(10^x\) 取模为 \(10^y\) 次方,那么我们只关注,怎么只操作n位数字的后x位,除了对倒数第y+1位要置1外,其他都置为0。我们将后y位数字里面为1的数量记录为time,如果要置1的位置的数字为0则操作数time+1,如果要置1的位置的数字位1,则重复操作,输出time-1.

#include
using namespace std;
int n, x, y, res, cnt;
string s;
int main() {
	//freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n >> x >> y;
	cin >> s;
	for (int i = n - 1, cnt = 0; cnt < x; i--) {
		if (cnt == y)res += s[i] != '1';
		else res += s[i] != '0';
		cnt++;
	}
	cout << res << endl;
}

B题:1165B

19级暑假第四场训练赛_第2张图片

Input

4
3 1 4 1

Output

3

Input

3
1 1 1

Output

1

Input

5
1 1 1 2 2

Output

2

思路

这题有两个方法,看到另一个同学的方法,感觉我的有些愚钝。
第一种,则是小到大排序,然后遍历数组,每遇到能满足做题数量的比赛,则天数加一。
第二种,则是用数组来记录有n道题的比赛数量,然后遍历一次数组,算是基数排序吧。

#include
using namespace std;
int main() {
	//freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	int n, i, p = 0;
	cin >> n; int arr[n];
	for (i = 0; i < n; i++)
		cin >> arr[i];
	sort(arr, arr + n);
	for (i = 0; i < n; i++)
		if (arr[i] > p)
			p++;
	cout << p;
}

C题:1165C

19级暑假第四场训练赛_第3张图片

Input

4
good

Output

0
good

Input

4
aabc

Output

2
ab

Input

3
aaa

Output

3

思路

这题的方法比较贪心,我们知道像abccd这种字符串,我们只需要删一个就能变成一个好字符串。可以是ab之一,也可以是c。对于连续的重复的字符串,只要它符合好字符串的规则,也就是前一个在偶数位置,后一个在奇数位置,那么就不需要特别处理。当然多与两个便一定要删减了。
我们可以采取贪心的策略,重新组合一个字符串。当我们取奇数位置时,可以随意取,取偶数位置时候,如果和奇数位置相同,则不取。

#include
using namespace std;
int main() {
	int  n, i; cin >> n;
	string s, ss; cin >> s;
	for (i = 0; i < n - 1;) {
		if (s[i] != s[i + 1]) {
			ss += s[i];
			ss += s[i + 1];
			i += 2;
		}
		else i++;
	}
	cout << n - ss.size() << endl << ss;
}

D题:1165D

19级暑假第四场训练赛_第4张图片

思路

模拟,利用set的性质存储输入的因子(自带排序),

排序

如果数组符合要求,那么这个X必定是最大最小值相乘可得

尝试对tmp求因子,如果所有的因子匹配上了即可输出。

#include
using namespace std;
typedef long long ll;
sets, ss;
int main() {
	//freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	ll t, n, tmp; cin >> t;
	while (t--) {
		cin >> n;
		s.clear(); ss.clear();
		for (ll i = 1; i <= n; ++i) {
			cin >> tmp;
			s.insert(tmp);
		}
		tmp = (*s.begin()) * (*s.rbegin());
		for (ll i = 2; i * i <= tmp; ++i) {
			if (tmp % i == 0) {
				ss.insert(i);
				ss.insert(tmp / i);
			}
		}
		if (s == ss)cout << tmp << endl;
		else cout << -1 << endl;
	}
}

E题:1165E

19级暑假第四场训练赛_第5张图片

19级暑假第四场训练赛_第6张图片

输入

5
1 8 7 2 4
9 7 2 9 3

输出

646

输入

1
1000000
1000000

输出

757402647

输入

2
1 3
4 2

输出

20

思路

我们知道,对于两个元素数量相同的数组,如何排序让它们的各位相乘求和的值最小,那么只能是

一个数组正序,一个数组倒序,然后逐个相乘求和。

对于这题,a和b只能移动b数组的顺序,那么其实是一样的,只要b第i大的数字对应a第i小的数字就

行了。但是题目给出了$ f(l,r)$这个函数,并且是所有这个函数相加。不难得到,在n个元素的数组中,对于第i个元素,必定有 $ i*(n-i+1)$个区间将这个元素包含在内,也就是说,和第 $i $个元素相关的乘积将出现以上次累加。

对于a这个数组来说,它是不能动的,何不先把这些累计的次数与a里的元素相乘呢。

我们将a内元素和对应累计次数相乘得到的新数组p 排正序,b排倒序,再逐位相乘相加,取个模,

那就是我们要求的答案了。

#include 
using namespace std;
typedef long long ll;
const int MOD = 998244353;
const int N = 2e5 + 100;
ll n, ans, a[N], b[N], q[N], k;//使用long long防溢出
bool cmp(int x, int y) {
	return a[x] > a[y];
}
int main() {
	freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n;
	//对于第i个元素,必定有i*(n-i+1)个区间将这个元素包含在内
	for (int i = 0; i < n; i++)cin >> a[i],a[i] *= (i + 1) * (n - i),q[i] = i;
	for (int i = 0; i < n; ++i)cin >> b[i];
	sort(q, q + n,cmp); sort(b, b + n);
	ans = 0;
	for (int i = 0; i < n; i++){
		k = (a[q[i]] % MOD * b[i]) % MOD;
		ans = (ans + k) % MOD;
	}
	cout << ans << endl;
}

F题:

CodeForces - 1165F1

F1、F2思路和AC代码 来自大牛 Jfeng666

题意

简单版和难版题目的区别只在于数据范围。
主人公Ivan,玩一个电脑游戏,包括很多微交易,可以得到让角色变得更帅的饰品,他想自己的角色看起来更酷,他想利用这些微交易得到饰品,并且他在得到所有东西后才开始玩这个游戏。
每一天早上,Ivan可以赚到游戏里一单位的burle(游戏货币)
有n种微交易,对于每一种微交易的花费,打折时每个饰品花费1单位burle,平时则花费2单位burle。对于第i个饰品,他想要进行ki次交易,订单将在晚上完成。
游戏商店有m次折扣,第j次输入(dj,tj)代表第t种商品在第d天打折。
Ivan想要尽早得到所有的物品,你的任务是计算最少经过多少时间他能买到所有想要数量的饰品,并开始游戏。
……………………有人说还是不懂我简单说下
主角想买一些东西,然后从第一天开始每天领一块钱
平时每件东西是两块,打折日对某些种类当天就一块。
给了你打折日,还有他想买的东西,给好种类和数量了
打折日也说明了日期和种类了
问你最短多少时间内能买完这些东西

思路

对于结果来说,最重要的是天数。如果主角想要n个物品,那么所需要天数必定在n~2n以内,我们只需要找到其中最小的可以买完东西的天数就行了。
对于固定的天数day,我们能接触到的打折活动必定在第day天及之前能接触到。那么对于同一件物品,打折的时间越后,能一次性买的数量越多。我们不妨在这天尽量买足所需数量。但是请注意,在第day天前举办的打折活动,并不能使用day个货币,也就是无法超前消费,那么,对于第day天剩余拥有的货币bday,和第fday天举办的活动(fday 同理,对于第5天和第3天的打折,假设第五天有五个货币,为了满足需求,我买了三个,那么对于第三天的打折,我最多只能买两个,假设对这天打折的需求也是三个,那么剩余一个必定要通过两元一个的原价购买。所以这样的贪心并不影响结果。

#include 
#include 
using namespace std;
#define maxn 200010
//该算法对于两个范围难度的题都能解决。
int lf, rt, b[maxn];
int ned[maxn], da[maxn], tp[maxn];
int k, p[maxn], n, m;
int cmp(int x, int y) {
    return da[x] < da[y];
}
int min(int a, int b) {
    return a < b ? a : b;
}
bool check(int v) {//测试v块钱能否买完
    for (int i = 1; i <= n; i++)
        b[i] = 1;
    //经过最后打折日的物品在前面打折日则不再购买,因为必定买完,或者剩下的需求没法满足
    int mon = v, cost = 0;
    for (int i = m; i > 0; i--)
        if (b[tp[p[i]]] && da[p[i]] <= v) {
            b[tp[p[i]]] = 0;
            mon = min(mon, da[p[i]]);
            cost += min(mon, ned[tp[p[i]]]);
            mon -= min(mon, ned[tp[p[i]]]);;
        } //只要低于v的折扣都能接触到,但能买的最大数量限制于打折时间和需求。
    return cost + (v - cost) / 2 >= lf;
}
int divide(int lf, int rf) {
    int mid;
    while (lf < rf) {
        mid = (lf + rf) / 2;
        if (check(mid)) rf = mid - 1;
        else lf = mid + 1;
    }
    //为了优化贪心找到答案的次数,我使用二分法。
    return check(rf) ? rf : rf+1;
    //因为退出循环时存在rf=lf和rf=lf-1的可能
}
int main()
{
    cin >> n >> m;
    lf=0;
    for (int i = 1; i <= n; i++){
        cin >> ned[i];
        lf += ned[i];
    } rt = lf * 2;
    for (int i = 1; i <= m; i++) {
        cin >> da[i] >> tp[i];
        p[i] = i;
    }
    sort(p + 1, p + 1 + m, cmp);
    cout << divide(lf, rt) << endl;
    return 0;
}

你可能感兴趣的:(19级暑假第四场训练赛)