《算法竞赛进阶指南》0.6倍增

109. 天才ACM

给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下:
从集合 S中取出 M 对数(即 2?M 个数,不能重复使用集合中的数,如果 S 中的整数不够 M 对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值就称为集合 S的“校验值”。
现在给定一个长度为 N的数列 A 以及一个整数 T。
我们要把 A分成若干段,使得每一段的“校验值”都不超过 T。
求最少需要分成几段。

输入格式
第一行输入整数 K,代表有 K组测试数据。
对于每组测试数据,第一行包含三个整数 N,M,T。
第二行包含 N个整数,表示数列A1,A2…AN。

输出格式
对于每组测试数据,输出其答案,每个答案占一行。

数据范围
1≤K≤12,
1≤N,M≤500000,
0≤T≤1018,
0≤Ai≤220

输入样例:
2
5 1 49
8 2 1 7 9
5 1 64
8 2 1 7 9

输出样例:
2
1

#include <iostream>
#include <algorithm>
#include <cstdio>

#define ll long long 

using namespace std;

const int N = 500010;
int n, m, mid;
ll k, a[N], b[N], c[N];

void merge(int l, int mid, int r)
{
    //if(r == n) break;
    int i = l, j = mid + 1;
    for(int k = l; k <= r; k++) // k = L
        if(j > r || (i <= mid && b[i] <= b[j])) c[k] = b[i++];
        else c[k] = b[j++];
     //赋值回数组b在下面
}

ll f(int l, int r) //求校验值 贪心取集合s中最大的M个数和最小的M个数,最大和最小配成一对...
{
    if(r > n) r = n;
    int small = min(m, (r - l + 1) >> 1);//求最小值,长度(r - l + 1)/ 2
    for(int i = mid + 1; i <= r; i++) b[i] = a[i]; //mid + 1 = 2 从2开始幅值,所以前面b[1] = a[1]
    sort(b + mid + 1, b + r + 1);
    merge(l, mid, r);
    ll ans = 0; //至少分几段
    for(int i = 0; i < small; i++)
        ans += (c[r-i] - c[l+i]) * (c[r-i] - c[l+i]); //每对数的差的平方之和
    return ans;
}

void Genius_ACM()
{
    cin >> n >> m >> k;
    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    int ans = 0, l = 1, r = 1;
    mid = 1;
    b[1] = a[1];
    while(l <= n)
    {
        int p = 1; //初始化步长p = 1
        while(p)
        {
            ll num = f(l, r + p); // 求校验值num
            if(num <= k) //不超过整数k
            {
                mid = r = min(r + p, n);
                for(int i = l; i <= r; i++) b[i] = c[i]; //int i = l 是L
                if(r == n) break;
                p <<= 1; // p = p * 2
            }
            else p >>= 1;
        }
        ans ++;
        l = r + 1;
    }
    cout << ans << endl;
}

int main()
{
    int t;
    cin >> t;
    while(t--) Genius_ACM();
    return 0;
}

原文地址:https://www.cnblogs.com/wmxnlfd/p/10861992.html

时间: 2024-10-27 08:16:24

《算法竞赛进阶指南》0.6倍增的相关文章

《算法竞赛进阶指南》学习总结 #include&lt;algorithm&gt;

今天下午大致学完了进阶指南中algorithm头文件下的内容,在这里进行一个总结.   reverse翻转   顾名思义,reverse进行的操作就是翻转原来的顺序,理解非常简单,故不赘述. 操作样例: #include<bits/stdc++.h> using namespace std; vector<int>a; int b[233]; int main() { int na,nb; //vector的实现 scanf("%d",&na); for

《算法竞赛进阶指南》学习总结 二分与三分

首先......我是一个很菜很菜的萌新,所以这篇文章写得很详细,有很多我自己的口水话方便我理解,请各位谨慎食用qwq 以前在网上找过很多介绍二分的博客,但都感觉对萌新不太友好,反正我当时连跳石头都没看懂,所以决定自己写一篇!其中有我的想法,也借鉴了书里的很多内容,感谢lyd. 二分答案,顾名思义,就是对我们所需要的答案进行二分,对我们要求的值进行二分.二分的基础用法是在单调序列或者单调函数当中查找,当答案具有单调性,我们就可以采用二分来计算,当然还有三分,在后面我会详细讲到 整数集合上的二分 在

《算法竞赛进阶指南》0.8总结与练习(1)

116. 飞行员兄弟 "飞行员兄弟"这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱. 已知每个把手可以处于以下两种状态之一:打开或关闭. 只有当所有把手都打开时,冰箱才会打开. 把手可以表示为一个4х4的矩阵,您可以改变任何一个位置[i,j]上把手的状态. 但是,这也会使得第i行和第j列上的所有把手的状态也随着改变. 请你求出打开冰箱所需的切换把手的次数最小值是多少. 输入格式 输入一共包含四行,每行包含四个把手的初始状态. 符号"+"表示把手处于闭合状态,而

bzoj 1787 &amp;&amp; bzoj 1832: [Ahoi2008]Meet 紧急集合(倍增LCA)算法竞赛进阶指南

题目描述 原题连接 Y岛风景美丽宜人,气候温和,物产丰富. Y岛上有N个城市(编号\(1,2,-,N\)),有\(N-1\)条城市间的道路连接着它们. 每一条道路都连接某两个城市. 幸运的是,小可可通过这些道路可以走遍Y岛的所有城市. 神奇的是,乘车经过每条道路所需要的费用都是一样的. 小可可,小卡卡和小YY经常想聚会,每次聚会,他们都会选择一个城市,使得3个人到达这个城市的总费用最小. 由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成. 他们

《算法竞赛进阶指南》0.4二分

102. 最佳牛围栏 农夫约翰的农场由N块田地组成,每块地里都有一定数量的牛,其数量不会少于1头,也不会超过2000头. 约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大. 围起区域内至少需要包含 F块地,其中 F会在输入中给出. 在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少. 输入格式 第一行输入整数 N和 F,数据间用空格隔开. 接下来 N行,每行输出一个整数,第i+1行输出的整数代表,第i片区域内包含的牛的数目.

算法竞赛进阶指南 走廊泼水节

原题链接 题目描述 给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树. 求增加的边的权值总和最小是多少. 输入格式 第一行包含整数t,表示共有t组测试数据. 对于每组测试数据,第一行包含整数N. 接下来N-1行,每行三个整数X,Y,Z,表示X节点与Y节点之间存在一条边,长度为Z. 输出格式 每组数据输出一个整数,表示权值总和最小值. 每个结果占一行. 数据范围 \(N \le 6000,Z \le 100\) 输入样例: 2 3 1 2 2 1

《算法竞赛进阶指南》打卡活动 #0x00 基本算法

101. 最高的牛 题目链接:https://www.acwing.com/problem/content/103/ 作为一个银牌水平的主演数据结构的演员来说,这题现在发现非常好想,每个牛分配一个优先度.我搞一个区间线段树,每次update中间一段使得他们的优先度整体下降到比两端中较小的优先度还要小.然后反过来按照优先度分配身高.哈哈!根本不需要什么算法.不过每次都不询问为什么要线段树呢?差分不香吗?当然不香,每次询问当前的优先度啊! const int MAXN = 10000; int re

【算法竞赛进阶指南】USACO07Tallest Cow

前缀和,利用左右端点操作代替对区间的操作,从而优化输入,最后进行一次前缀和的操作,求得结果,这道题里面有个很关键的问题,就是需要去重,本来我想用set,但貌似有点鬼畜,算了,利用map去重,还有pair类型(学一下) #include <iostream> #include <algorithm> #include <map> #include <utility> using namespace std; map<pair<int,int>

【算法竞赛进阶指南】POJ1845Sumdiv

本题目是一道数论的综合题目,主要的一个点就是利用二分法来求等比数列的和,其余的点就是唯一分解定理分解质因数,还有就是快速幂 A^B约数和\(=(1+p_{1}+p_{1}^{2}+\cdot\cdot\cdot+p_{1}^{c1})*(1+p_{2}+p_{2}^{2}+\cdot\cdot\cdot+p_{2}^{c2})*\cdot\cdot\cdot*(1+p_{n}+p_{n}^{2}+\cdot\cdot\cdot+p_{n}^{cn})\) 如果c是奇数(一共有偶数项)\(sum(