ErikTse2023Codeforces思维提升赛(1)

A. Consecutive Points Segment(连续点段)- 1671B

TLE:2s t:2e4 n:2e5 xi:2e6

题目大意

一串严格递增的数组,在x轴上,给定 能否使得n个点,对于每个点可以进行至多一次操作,即向左移动一格,或向右移动一格,问最终n个点相邻。

思路

首先我们要分析出,对于一段连续的点,为了保持他们的连续性,就必须作为一个整体全部向左移动一 格或全部向右移动一格。 于是我们可以从整个 轴上划分出若干条线段,并且我们知道最左侧的线段不可能左移,只能右移,最 右边的线段不可能右移,只能左移,并且移动最多一格。 换个角度想,最左边的线段往右边移动一格,会使得“空隙”减少一格,最右边的线段往左边移动一格, 会使得“空隙”减少一格,但是中间的线段移动是不会改变“空隙”个数的。 也就是说只要这个 代码 轴上的“空隙”数量,就一定可以操作使得所有点相邻,否则就不行。

思考

在考虑单个点的操作时,可以将其转化成多个点的共同操作,并且要 遵守某个原则(在这道题当中是保持连续性),并且将每次操作所产生的影响,用一个更容易量化的东 西来表达(这道题中就是中间空隙的个数)。

代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int n;
int a[200010];

void solve() {
	int cnt = 0;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) 
		scanf("%d", &a[i]);
	for (int i = 2; i <= n; i++)
		cnt += a[i] - a[i - 1] - 1;
	if (cnt <= 2) 
		printf("YES\n");
	else 
		printf("NO\n");
}


int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		solve();
	}
	return 0;
}
B. Make it Increasing - 1667A

TLE:2s n:5000 ai:1e9

题目大意

给定一个长度为n的数组,现在有一个长度为n初始全为0的数组 b。

每次操作可以选择一个下标,并使得bi += ai 或  bi -= ai。

问最少操作多少次可以使得数组严格升序。

思路

因为n范围只到5000,所以支持n2做法,为了使得数组b严格升序,那么肯定是存在某个点 。[1,k]都做减法操作,[k+1,n]都要做加法操作 ,k属于[0,n],我们设一个数组c,表示操作情况,例如ci = 3 ,表示对bi做了3次+=ai的操作,ci = -3 ,表示对bi做了3次-=ai的操作。

那么这个c数组中就必然存在一个0。

例如c={-3,-5,-1,5,7},此时的操作次数为,但是其实我们完全可以将左半部分全部都减少次操作,

可以将左半部分减少一次操作,c={-2,-4,0,5,7}或右半部分减少5次操作c={-2,-4,1,0,2},这两种方案的操作次数肯定不比原先的操作差的。于是,我们只需要枚举数组中的位置,然后从这个位置分别向左和向右计算出整个c数组。对于每个c数组,计算一个操作次数,将所有情况的操作次数取小即可。

思考

代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
typedef long long ll;
ll n;
ll inf = 8e18;
ll a[5010], b[5010];

ll min1(ll a, ll b) {
    if (a >= b) {
        return b;
    }
    return a;
}

int main() {
    scanf("%lld", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &a[i]);
    }
    ll ans = inf;
    for (int i = 1; i <= n; ++i) {
        memset(b, 0, sizeof(ll) * (n + 1));
        ll cnt = 0;
        for (int j = i - 1; j >= 1; --j) {
            ll k = 1 - b[j + 1]/ a[j]; // -a1 * c1 < b2 -> c1 > -b2 / a1 + 1
            b[j] = -a[j] * k; //更新
            cnt += k;
        }
        for (int j = i + 1; j <= n; ++j) {
            ll k = b[j - 1] / a[j] + 1; // a2 * c2 > b1 -> c2 >= b1 / a2 + 1
            b[j] = a[j] * k; // 更新b
            cnt += k;
        }
        ans = min1(ans, cnt);
    }
    printf("%lld\n", ans);
    return 0;
}
C. Permutation -359B
题目大意

思路

式子认真理解后,即使奇数项减去后一项(偶数项)的绝对值,在减去奇数项减去后一项偶数项的差的和的绝对值,对于绝对值我们想的就是如何消去绝对值,此时可以考虑到,当数列为倒叙数列的时候,可以消去绝对值符号且相间的结果为0,这是一个很重要的属性,且每颠倒一项奇数偶数的组合就会使相减的结果+2,即k+1。因为此时第一个和式的结果没变化,第二个和式从1变为-1,也就是说整体会从减1变为减-1。并且题目数据保证0<= 2k <= n

思考

于是我们只需要开倒叙数组然后颠倒k次即可

代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
typedef long long ll;

ll a[1000010];
int k, n;

int main() {
    scanf("%d %d", &n, &k);
    for (int i = 1; i <= 2 * n; i++) {
        a[i] = 2 * n - i + 1;
    }
    for (int i = 1; i <= k; i++) {
        int tem = a[2 * i - 1];
        a[2 * i - 1] = a[2 * i];
        a[2 * i] = tem;
    }
    for (int i = 1; i <= 2 * n; i++) {
        printf("%lld ", a[i]);
    }
    return 0;
}
D. Lucky Chains - 1766D
题目大意

定义二元组(x,y)是幸运的,当gcd(x,y) = 1时,即x,y互质

定义由(x,y)的幸运链:长度为k+1,如果

(x + 1,y + 1),(x + 2,y + 2)......(x + k,y + k)都为幸运链,每次给定一个(x,y)求他生成幸运链的最长的长度

思路

这道题拿到手不好想,因为涉及到数论知识,但是其实考察并不深入我们可以假设从(x,y)出发,中间都是幸运的,直到gcd(a + t,y + t)!=1就停利用gcd的性质gcd(a,b) = gcd(a,b - a)可以知道

gcd(x +t,y+t)= gcd(a +t,y-x)!=1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值