首页 > 其他分享 >CF1921F Sum of Progression

CF1921F Sum of Progression

时间:2024-01-16 11:55:23浏览次数:35  
标签:pre end Progression int Sum CF1921F times 块长 include

题目链接:CF

一道经典的类型题,把这种类型的题拿出来单独说一下。

注意到问题中涉及到需要维护 \(a_{x+k\times step}\) 这样的信息,这样的信息很难用树型结构维护,比较容易用块级结构维护,我们注意到其实是每次这种步长 \(+step\) 的信息很难维护,我们考虑一类特殊的分块:如果 \(step\) 恰好为块长。

我们会惊讶地发现,从某个位置往后不断地走 \(step\) 就是说不断地恰好跨过一个块长,它的路径上的每个点的信息在块里都是同一个位置,这样一来其实我们可以为每一种块长分块的结果维护它特有的一些信息。

注意到一点,在预处理时,每一种块长如果我们都暴力的情况下,从小块长开始,比如从块长为 \(1\) 开始,显然处理的复杂度是 \(O(n)\),这样一来,其实我们能处理最多 \(\sqrt{n}\) 种块长,预处理的复杂度最多为 \(n\sqrt{n}\),如果更长的块长我们注意到,如果暴力地从 \(1\) 位置开始跳,那么最多也就跳 \(\sqrt{n}\) 次,此时每个 \(\ge \sqrt{n}\) 的块长其实是都是能直接暴力处理,这启发我们使用根号分治解决这类问题:

  1. \(块长 < \sqrt{n}\),考虑为这类所有块长分块维护统一的信息。

  2. \(块长 \ge \sqrt{n}\),考虑直接暴力。

这就是这类问题的模版框架。如果出现修改,我们对于查询的调整可能发生调整,比如有时候查询的是具体的值,那么可以考虑每种块长的贡献即可,最后会给出例题。

回到本题,我们需要对小块长维护怎样的信息,考虑到查询直接维护 \(a_i \times 当前查询中的下标\) 是困难的,注意到阿贝尔求和的方式,我们可以把它拆成若干个前缀和做差的方式:

\[ a_{s}\times 1+a_{s+d}\times 2+a_{s+2d}\times 3+a_{s+3d} \times 4....a_{s+(k-1)\times d}\times k \]

先考虑简单情形:

如果 \(d==1\):

\[上式=a_s+2\times a_{s+1}+3\times a_{s+2}....k \times a_{s+k-1} \]

类似于阿贝尔求和假如知道:

\[pre[x]=a_{1}+....a_s+a_{s+1}+a_{s+2}+....a_{x} \]

那么很显然有原式子拆成如下图求和:

可以看出每一行都是一个区间和,借助前缀和,不妨设当前查询的最后一个数为 \(end\):

\[ 原式=(pre[end]-pre[s-1])+(pre[end]-pre[s-2])+.....(pre[end]-pre[end-1]) \]

\[=k\times pre[end]-(pre[s-1]+pre[s-2]....pre[end-1]) \]

很容易看出,前面其实就为一个前缀和,后面则是“前缀和数组”的区间和形式,显然维护前缀和数组的前缀和就能办到了。

现在抽象一下,如果 \(d \neq 1\),怎么解决:

事实上,我们把上式改写一下:

\[pre[d][idx]=a_{start}+a_{start+d}+a_{start+2d}+....a_{idx} \]

其中 \(start\) 为从 \(idx\) 往前不断走 \(d\) 长度能达到的最早起点,换句话来说,就是最开始我们提到的那张图:

\(idx\) 此时可能不位于第一个块处,比如 \(idx=x_2\),那么 \(x_1\) 就是它在第一个块的相对位置,也即是 \(start\),我们维护的就是这一步所有 \(x\) 之和。

那么上面的式子显然可以改写为:

\[k\times pre[end]-(pre[s-d]+pre[s]+pre[s+d]+pre[s+2d]....pre[end-d]) \]

这里的 \(pre\) 第一维取 \(d\),省略第一维为上述形式。那么我们可以维护两个数组:

  1. \(pre[d][idx]\) 表示 \(d\) 长度的块的这些 \(x\) 的前缀和信息。

  2. \(Prepre[d][idx]\) 表示 \(d\) 长度的这些 \(pre[d][idx]\) 的前缀和信息。

其中易知道 \(Prepre[d][idx]=pre[d][start]+pre[d][start+d].....pre[d][idx]\)。
那么上述式子就可以轻易改写了:

\[=k \times pre[d][end]-(Prepre[d][end-d]-Prepre[d][s-2\times d]) \]

其实只需要注意到,前缀和作差中后一项不再是 \(-1\),改为 \(-d\) 即可。

最后的细节

注意到有类似 \(s-2\times d\) 这类下标,我们最好和 \(0\) 取个 \(max\),防止出现负数,究其原因可能这个块就是第一项了。然后注意到块比较小的时候,如果 \(k\) 比较小也可以暴力,那么我们可以对这两类情况同时走暴力分支也行。当然改写成后缀和也是可以的,不用怎么需要考虑越界问题,因为超过的部分你数组开大点直接能取值为 \(0\) 的后缀和了。记得多测情空。

带注释参考代码
#include <bits/stdc++.h>

// #pragma GCC optimize("Ofast,unroll-loops")
// #pragma GCC optimize(2)

// #define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>

#endif

using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

template <typename T>
int disc(T* a, int n)
{
    return unique(a + 1, a + n + 1) - (a + 1);
}

template <typename T>
T lowBit(T x)
{
    return x & -x;
}

template <typename T>
T Rand(T l, T r)
{
    static mt19937 Rand(time(nullptr));
    uniform_int_distribution<T> dis(l, r);
    return dis(Rand);
}

template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
    return (a % b + b) % b;
}

template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
    a %= c;
    T1 ans = 1;
    for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
    return modt(ans, c);
}

template <typename T>
void read(T& x)
{
    x = 0;
    T sign = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    x *= sign;
}

template <typename T, typename... U>
void read(T& x, U&... y)
{
    read(x);
    read(y...);
}

template <typename T>
void write(T x)
{
    if (typeid(x) == typeid(char))return;
    if (x < 0)x = -x, putchar('-');
    if (x > 9)write(x / 10);
    putchar(x % 10 ^ 48);
}

template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
    write(x), putchar(c);
    write(c, y...);
}


template <typename T11, typename T22, typename T33>
struct T3
{
    T11 one;
    T22 tow;
    T33 three;

    bool operator<(const T3 other) const
    {
        if (one == other.one)
        {
            if (tow == other.tow)return three < other.three;
            return tow < other.tow;
        }
        return one < other.one;
    }

    T3() { one = tow = three = 0; }

    T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
    {
    }
};

template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
    if (x < y)x = y;
}

template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
    if (x > y)x = y;
}

constexpr int N = 1e5 + 10;
constexpr int Size = ceil(sqrt(N));
constexpr int CNT = (N + Size - 1) / Size + 1;
int n, q;
ll a[N];
ll pre[CNT + 1][N];
ll Prepre[CNT + 1][N];

inline void solve()
{
    cin >> n >> q;
    forn(i, 1, n)cin >> a[i];
    int siz = sqrt(n);
    forn(i, 1, siz)
    {
        forn(j, 1, n)
        {
            pre[i][j] = a[j];
            if (j > i)pre[i][j] += pre[i][j - i];
            Prepre[i][j] = pre[i][j];
            if (j > i)Prepre[i][j] += Prepre[i][j - i];
        }
    }
    forn(i, 1, q)
    {
        ll s, d, k;
        cin >> s >> d >> k;
        ll ans = 0;
        if (k <= siz or d >= siz)forn(i, 1, k)ans += a[s + (i - 1) * d] * i;
        else
        {
            int end = s + (k - 1) * d;
            int endpre = end - d;
            ans = k * pre[d][end] - (Prepre[d][endpre] - Prepre[d][max(s - 2 * d, 0ll)]);
        }
        cout << ans << " ";
    }
    forn(i, 1, siz)forn(j, 1, n)pre[i][j] = Prepre[i][j] = 0;
    cout << endl;
}

signed int main()
{
    // MyFile
    Spider
    //------------------------------------------------------
    // clock_t start = clock();
    int test = 1;
    //    read(test);
    cin >> test;
    forn(i, 1, test)solve();
    //    while (cin >> n, n)solve();
    //    while (cin >> test)solve();
    // clock_t end = clock();
    // cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

\[最终单个\ Test\ 的时间复杂度为预处理加若干次查询: O(n\sqrt{n}+q\sqrt{n}) \]

推荐经典题:P5309 [Ynoi2011] 初始化 题解

标签:pre,end,Progression,int,Sum,CF1921F,times,块长,include
From: https://www.cnblogs.com/Athanasy/p/17967352

相关文章

  • abc336 E - Digit Sum Divisible 题解 数位DP
    题目链接:https://atcoder.jp/contests/abc336/tasks/abc336_e题目大意:我们定义一个整数\(n\)的数位和为\(n\)的十进制表示中的各位上的数字之和。比如:整数\(2024\)的数位和为\(2+0+2+4=8\)。一个正整数\(n\)被称作一个好数如果\(n\)能被它的数位和整除......
  • PostgreSQL 数据库安全之检验数据块的损坏- data_checksums 参数设置
    默认情况下,数据页不受校验和保护,但可以选择为集群启用这一功能。启用后,每个数据页都包含一个校验和,该校验和在写入该页时更新,并在每次读取该页时进行验证。只有数据页受校验和保护;内部数据结构和临时文件不是。校验和通常在使用initdb初始化集群时启用。还可以在以后的脱......
  • 并行 sha256sum 命令
    之前为文件夹里的文件生成SHA-256摘要时,我使用的是sha256sum*.mp4*.xml*.jpg>sha256sums.txt这个命令是逐个生成哈希值的,在计算完成1.mp4之前并不会开始计算2.mp4,不能很好得利用多核性能。解决办法也很简单,利用“百闻不如一见”的xargs即可:echo*.mp4*.xml*.jp......
  • 关于函数式接口中常用的Supplier、Consumer、predicate、Function的总结以及其使用场
    首先介绍一下函数式接口:函数式接口在Java中是指:有且仅有一个抽象方法的接口。函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导......
  • 测试SuspendThread、ResumeThread
    #include<iostream>#include<windows.h>#include<process.h>#include<conio.h>enum{ EVT_PAUSE=0, EVT_RESUME, EVT_QUIT, EVT_TOTAL};staticHANDLEevents[EVT_TOTAL]={NULL,NULL,NULL};staticunsignedint__stdcallhe......
  • CF1270G Subset with Zero Sum
    G.SubsetwithZeroSum很妙。一开始冲着背包去想的,显然不行。考虑他条件给的这个\(i−n\lea_i\lei−1\)化简一下得到\[1\lei-a_i\len\]题目要去求\[\sum\limits_{i\inS}a_i=0\]把所给信息往这个式子上靠。得到\[\sum\limits_{i\inS}i=\sum......
  • 天翼云亮相操作系统大会&openEuler Summit 2023,斩获多项大奖!
    近日,由开放原子开源基金会等主办,以“崛起数字时代引领数智未来”为主题的操作系统大会&openEulerSummit2023在北京举行。大会邀请院士、产业组织及全球开源基金会代表、学术领-袖、领先行业代表、技术专家等1000+位海内外嘉宾,共探操作系统产业发展方向和未来机遇。会上,天翼云......
  • 天翼云亮相操作系统大会&openEuler Summit 2023,斩获多项大奖!
    近日,由开放原子开源基金会等主办,以“崛起数字时代引领数智未来”为主题的操作系统大会&openEulerSummit2023在北京举行。大会邀请院士、产业组织及全球开源基金会代表、学术领袖、领先行业代表、技术专家等1000+位海内外嘉宾,共探操作系统产业发展方向和未来机遇。会上,天翼云荣获......
  • WAVE SUMMIT+ 2023倒计时2天,传文心一言将曝最新进展!
    10句话2分钟,挑战成功说服宿管阿姨开门,这个人群中的“显眼包”是一个接入文心大模型4.0游戏里的NPC,妥妥“工具人”实锤~尝试用AI一键自动识别好坏咖啡豆,看一眼便知好坏,真正“颜值即正义”,让咖啡星人狂喜~借助AI在任何平面上模拟的钢琴,即兴“弹奏”世界名曲,开一场科技感满满的专属演奏......
  • AI原生应用开发“三板斧”亮相WAVE SUMMIT+2023
    面对AI应用创新的风口跃跃欲试,满脑子idea,却苦于缺乏技术背景,不得不望而却步,这曾是许多开发者的苦恼,如今正在成为过去。12月28日,WAVESUMMIT+深度学习开发者大会2023在北京举办。百度AI技术生态总经理马艳军最新发布飞桨开源框架2.6版本和大模型重构的开发工具链,包括全新发布Comate......