首页 > 其他分享 >有用的技巧(前缀、差分、位运算、快速幂、倍增)

有用的技巧(前缀、差分、位运算、快速幂、倍增)

时间:2023-01-06 00:22:57浏览次数:40  
标签:10 le 前缀 int 差分 st -- 区间 倍增

前缀和

一维前缀和

		for (int i = 1; i <= n; ++i)
         {
            pre[i] = pre[i - 1] + a[i];
         }

二维前缀和

		for (int i=1;i<=n;++i)
         {
            for (int j=1;j<=m;++j)
            {
                pre[i][j] = g[i][j]+pre[i][j-1]+pre[i-1][j]-pre[i-1][j-1];
            }
         }
         sum = pre[x2][y2]-pre[x2][y1-1]-pre[x1-1][y2]+pre[x1-1][y1-1];

差分

一维差分

		for (int i=1;i<=n;++i)
         {
            d[i] = a[i]-a[i-1];  //一维差分数组
         }	

二维差分

		int a[n+1][m+1];
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= m; ++j)
            {
                d[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];    //二维差分数组
            }														//减去左边、上边,加上左上角
        }
        //区间值的变化
        d[x1][y1] += x;
        d[x1][y2 + 1] -= x;
        d[x2 + 1][y1] -= x;
        d[x2 + 1][y2 + 1] += x;
        for (int i=1;i<=n;++i)
        {
            for (int j=1;j<=m;++j)
            {
                d[i][j] = d[i][j]+d[i][j-1]+d[i-1][j]-d[i-1][j-1];             //二维前缀和还原成原数组
            }
        }

位运算

1.取第K位,x&(1<<(k-1))

2.~x\(\Leftrightarrow\)x!=-1

首先负数的二进制在计算机中以补码的形式存在,那么一个负数是怎么转变为补码的呢?

负数二进制的衍变过程: 原码-->反码-->补码

以-1为例

什么叫做原码?原码就是使最高位变为1,最高位也叫做符号位

0000 0001 --> 1000 0001

什么叫做反码?就是除了符号位之外,其余位全部取反

1000 0001 --> 1111 1110

什么叫做补码?就是在反码的基础上+1

1111 1110 --> 1111 1111

所以-1的补码就是255

那么~x,如果x==-1,则 ~x返回0;如果x!=-1,则 ~x返回1,所用 ~x 等价于 x !=-1

3.取一个整数最低位的1所代表的数,\(lowbit(x) = x与(-x);\)

引申:利用\(lowbit\)运算统计一个整数二进制形式下1的个数,同时也是树状数组的实现原理

原理:我们利用lowbit运算出最低位1代表的数,然后用原数减去这个数,往复循环,直到原数变为0

int cnt = 0;
while (x)
{
	x -= x&(-x);
	cnt++;
}

倍增

RMQ问题

ST表

ST表用来实现可重复贡献问题的数据结构,什么叫做可重复贡献问题?比如\(max(x,x)=x,gcd(x,x)=x\),所以RMQ问题和区间GCD就是一个可重复贡献问题,但是像区间和就不是了,因为会有重叠部分加到结果中,这部分结果是我们不愿意看到的,当然操作\(opt\)必须具有结合律,或者说自反性

ST 表模板题

题目大意:给定n个数,有q个询问,对于每个询问,你需要回答区间\([l,r]\)中的最大值。

考虑暴力做法。每次都对区间扫描一遍,求出最大值。

显然,这个算法会超时。

ST表基于倍增思想,预处理\(O (nlogn)\),询问\(O(1)\),但是不支持修改

我们令\(st[i][j]\)代表区间\([i,i+2^j-1]\)中的最大值

显然\(st[i][0]=a[i]\)

根据定义式,一步可以跳\(2^j-1\)步,我们可以知道一个区间$[i,i+2^j-1] $

可以二分为两个区间\([i,i+2^{j-1}-1]\)和\([i+2^{j-1},i+2^{j}-1]\)

所以一个状态可以有前一个状态推出,我们直接列出状态转移方程

\[st[i][j]=max(st[i][j-1],st[i+2^{j-1}][j-1]) \]

以上就是预处理部分,复杂度\(O (nlogn)\)

假设 n=1e6

int st[n][22];
for (int j=1;j<=22;++j)
{
	for (int i=1; i+(1<<(j-1))-1<=n ;++i)
	{
		st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	}
}

对于询问:

回答区间\([l,r]\)中的最大值,因为是可重复贡献问题,所以我们只要用两个区间覆盖需要查询的区间即可,假设区间长度\(len=log_2(r-l+1)\)即把区间\([l,r]\)分为\([l,l+2^{len}-1]\)和\([r-2^{len}+1,r]\)

这两个区间一定能把\([l,r]\)覆盖,保证答案的正确性,我们直接列出回答

\[ans = max(st[l][len],st[r-(1<<len)+1][len]) \]

题目背景

ST 表经典题——静态区间最大值

请注意最大数据时限只有 0.8s,数据强度不低,请务必保证你的每次查询复杂度为 \(O(1)\)。若使用更高时间复杂度算法不保证能通过。

快速读入作用仅为加快读入,并非强制使用。

题目描述

给定一个长度为 \(N\) 的数列,和 $ M $ 次询问,求出每一次询问的区间内数字的最大值。

输入格式

第一行包含两个整数 \(N,M\),分别表示数列的长度和询问的个数。

第二行包含 \(N\) 个整数(记为 \(a_i\)),依次表示数列的第 \(i\) 项。

接下来 \(M\) 行,每行包含两个整数 \(l_i,r_i\),表示查询的区间为 \([l_i,r_i]\)。

输出格式

输出包含 \(M\) 行,每行一个整数,依次表示每一次询问的结果。

样例 #1

样例输入 #1

8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8

样例输出 #1

9
9
7
7
9
8
7
9

提示

对于 \(30\%\) 的数据,满足 \(1\le N,M\le 10\)。

对于 \(70\%\) 的数据,满足 \(1\le N,M\le {10}^5\)。

对于 \(100\%\) 的数据,满足 \(1\le N\le {10}^5\),\(1\le M\le 2\times{10}^6\),\(a_i\in[0,{10}^9]\),\(1\le l_i\le r_i\le N\)。

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define endl '\n'
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e5 + 10;
int st[N][22];
int main(void)
{
    Zeoy;
    int t = 1;
    // cin >> t;
    while (t--)
    {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n; ++i)
            cin >> st[i][0];
        for (int j = 1; j <= 22; ++j)
        {
            for (int i = 1; i + (1 << j) - 1 <= n; ++i)
            {
                st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
            }
        }
        while (m--)
        {
            int l, r;
            cin >> l >> r;
            int len = floor(log(r - l + 1) / log(2));
            cout << max(st[l][len], st[r - (1 << len) + 1][len]) << endl;
        }
    }
    return 0;
}

标签:10,le,前缀,int,差分,st,--,区间,倍增
From: https://www.cnblogs.com/Zeoy-kkk/p/17029243.html

相关文章

  • 压力应变桥信号处理系列隔离放大器差分输入转换0-10mV/0-20mV/0-±10mV/0-±20mV
    ​主要特性DIN11IPO压力应变桥信号处理系列隔离放大器是一种将差分输入信号隔离放大、转换成按比例输出的直流信号导轨安装变送模块。产品广泛应用在电力、远程监控、仪器......
  • 记录一道贪心+差分的题目
    由于差分这个知识点经常会被本人忘记,可能是做的题目太少了没怎么碰到。。。因此记录一道贪心加上差分的题目。本题为第十三届蓝桥杯省赛C++C组题目:4655.重新排序具体思......
  • Abp 框架统一设置表名前缀和 Schema
    Abp框架统一设置表名前缀、Schema太长不看版:对于内置表,在所有启动项目(不包括XXX.DbMigrator)的Program.cs文件和XXXDbContextFactory中,设置AbpCommonDbProperties......
  • [LeetCode]014-最长公共前缀
    >>>传送门题目编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串""。示例示例1输入:strs=["flower","flow","flight"]输出:"fl"......
  • 高精度+前缀和+差分
    高精度+前缀和+差分高精度高精度加法大整数存储:将数字存到数组里,第一个位置存个位,第二个位置存十位......从\(A_0+B_0\)开始算起,算个位,满十进一易错点:将数字存在......
  • 【LeeCode】14. 最长公共前缀
    【题目描述】编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ​​""​​。​​​​https://leetcode.cn/problems/longest-common-prefix......
  • 前缀和,差分
    前缀和,差分通俗理解前缀和听起来好高级啊,那么他究竟是什么啊?当询问一个区间[l,r]的和sum(忽略掉O(n)的暴力,它就发挥了大用处。基本的前缀和如下:s[i]=s[i-1]+a[i]......
  • 常用数据分析方法:方差分析及实现!
     Datawhale干货 作者:吴忠强,Datawhale优秀学习者,东北大学一个复杂的事物,其中往往有许多因素互相制约又互相依存。方差分析是一种常用的数据分析方法,其目的是通过数据分析......
  • 差分数组 前缀和数组
    小结:1、有数组d=[1,2,3,4,5,6],对d[2]到d[4]之间的所有数加上3,变为d=[1,2,6,7,8,6],那么差分数组也就从[1,1,1,1,1,1]变成了[1,1,4,1,1,-2]  Leetcode刷题笔记——差......
  • 本地化差分隐私随机扰动(回推)机制代码详解(K-RR)
    https://blog.csdn.net/ruiqu1650914788/article/details/127516602?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167245493516782427447943%2522%252C%2522......