测试6(下)

4.AtCoder abc288D - Range Add Query
题意:
给定n个数和k,若干次查询l,r范围内
是否可以对连续 k 个元素添加任意值 c,使子数列中的所有元素都为零,执行 0 次或多次。
思路:
因为是连续的区间加,假设sum[i]表示下标对 k取模为i的所有数的和。那每次操作就是将 sum的所有数都 + x
那最终为 0的充分条件就是 sum的所有数都是一样的。反过来,也是必要条件。
因此对于每组询问,统计该序列的下标对k取模的所有数的和,看看是否为同一个数即可。
预处理原数组的下标取模前缀和,每组询问就两个前缀和相减就得到该区间的下标取模前缀和
(答案好像没有问题,可惜时间超限,只能留到以后再解决)

#include <iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
ll a[maxn], tmp[maxn];
int main() {
    ios::sync_with_stdio(false);
    int n, k;
    cin >> n >> k;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        if (i >= k)          //前缀和变式
            a[i] += a[i - k];
    }
    int q;
    cin >> q;
    while (q--) {
        int l, r;
        cin >> l >> r;
        l--, r--;
        memset(tmp, 0, sizeof(tmp));
        int pos = r;
        bool  flag=0;
        for (int i = 0; i < k; ++i) {
            tmp[pos % k] = a[pos];
            pos--;
        }
        pos = l - 1;
        for (int i = 0; i < k && pos >= 0; ++i) {
            tmp[pos % k] -= a[pos];
            pos--;
        }
        for (int i = 1; i < k; i++){
            if (tmp[i] != tmp[i-1])
                flag = 1;
        }
        if (!flag)
            cout << "YES" << '\n';
        else
            cout << "NO" << '\n';
    }
    return 0;
} 


 6.AtCoder abc288E - Wish List
 题意:
 给定n个商品,价格ai,同时给定一个长度为n的数组c
 购买规则:购买序号为i的商品,其标号是未买商品的第j,其购入价格为 ai + cj
​需要要买m个物品,分别是 xi
 可以买不需要的物品。求购买所需物品的最小花费。
 思路:
如果要买当前这个物品i,则必须要支付ai,c值是不确定的,所以我们要使c值最小,假设当前在1到i-1之间的物品
中购买了j-1个物品,因为第i个物品对于前面的物品都没有影响,所以可以在任何时候购买第i个物品,
那么要多支付的价钱为i-j+1到i的c的min

#include<iostream>
using namespace std;
typedef long long ll;
const ll inf= 1e18;
const int maxn = 5e3 + 10;
int n, m, x, vt = 0;
int a[maxn], c[maxn], must[maxn], t[maxn][maxn];
long long ans = inf, dp[maxn][maxn];
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++) {
		cin >> c[i];
	}
	for (int i = 1; i <= m; i++) {  //must标记要购买的物品 
		cin>>x; 
		must[x] = 1;
	}
	for (int i = 1; i <= n; i++) {
		t[i][i] = c[i];
		for (int j = i + 1; j <= n; j++) {
			t[i][j] = min(t[i][j - 1], c[j]);
		}
		dp[0][i] = inf;  //设为无穷大
	}
	for (int i = 1; i <= n; i++) {//dp[i][j]表示枚举到第i个物品,一共买了j个物品的最小花费
		for (int j = 1; j <= n; j++) {
			if (must[i]) // 如果当前的物品为需要的
				dp[i][j] = dp[i - 1][j - 1] + a[i] + t[i - j + 1][i];
			else // 如果当前的物品为不需要的
				dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1] + a[i] + t[i - j + 1][i]);
		}
		if (must[i]) vt = 1;
		if (vt) dp[i][0] = inf;
	}
	for (int i = m; i <= n; i++) {
		ans = min(ans, dp[n][i]);
	}
	cout << ans << '\n';
	return 0;
}


7.CodeForces 1786C.Monsters(easy version)
题意:
 给定n个怪物,血量为ai,可以若干次施展操作1:对任何一个活着的怪物造成1点伤害。
 一次施展操作2:对所有活着的怪物造成1点伤害。如果至少有一只怪物因这一行动而死亡,那么就重复这一行动
 求杀死所有怪物,操作1施展的最小次数
 思路:
通过操作1使每个怪物血量差1

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 10;
int t,n;
ll ans;
ll a[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        sort(a + 1, a +1+ n);
        ans = 0;
        for (int i = 1; i <= n; i++){
            if (a[i] - a[i - 1] > 1){
                ans += (a[i] - a[i - 1] - 1);
                a[i] = a[i - 1] + 1;
            }
        }
        cout << ans << endl;
    }

    return 0;
}


CodeForces 1786E    E.Monsters(hard version)
 题意:
ard版本就是求数组的每一个前缀使用第一种攻击的最少次数是多少
(easy版就是求整个数组使用的第一种攻击最少是多少)。
这道题想了很久,但代码还是不能AC,于是到网上借鉴一下,发现需要用到线段树,
 只能明天学一下线段树,再来看一下这道题
 

#include <iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int a[N];
struct Node {
    int l, r, maxv, add;
}tr[N * 4];
void build(int u, int l, int r) {
    if (l == r) {
        tr[u] = { l, r, -l, 0 };
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        tr[u] = { l, r, max(tr[u << 1].maxv, tr[u << 1 | 1].maxv), 0 };
    }
}
void pushdown(int u) {
    if (tr[u].add) {
        tr[u << 1].add += tr[u].add;
        tr[u << 1].maxv += tr[u].add;
        tr[u << 1 | 1].add += tr[u].add;
        tr[u << 1 | 1].maxv += tr[u].add;
        tr[u].add = 0;
    }
}
void modify(int u, int l, int r, int c) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].maxv += c;
        tr[u].add += c;
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, c);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
        tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
    }
}
int query(int u) {
    if (tr[u].l == tr[u].r) return tr[u].l;
    pushdown(u);
    if (tr[u << 1].maxv > 0) return query(u << 1);  // 左边存在大于0的值,那么取左边找
    return query(u << 1 | 1);   // 大于0的值在右边
}
void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
    }
    build(1, 1, n);
    int cnt = 0;    // 当前维护的数的个数
    LL s = 0;   // 当前维护的数的总和
    for (int i = 1; i <= n; i++) {
        cnt++, s += a[i];   // 先记录
        modify(1, a[i], n, 1);  // a[i]的个数加1,si ~ sn都加1,si-i ~ sn-n都加1
        if (tr[1].maxv > 0) {   // 存在si-i大于0的数,需要删除最小的那个数
            int x = query(1);   // 找到最小的那个数
            modify(1, x, n, -1);    // 删除这个数,区间减1
            cnt--, s -= x;  // 删除最小的那个数
        }
        printf("%lld ", s - cnt * (cnt + 1ll) / 2); // 公式计算答案
    }
    printf("\n");
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值