8.3 学习笔记 前缀和与差分+双指针位+位运算+离散化+区间合并

前缀和与差分

AcWing 795.前缀和
一维前缀和
S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]
#include<iostream>
#include<cstdio>
using namespace std;

const int N = 100010;

int n,m;
int a[N],s[N];

int main()
{
    scanf("%d%d",&n, &m);
    for(int i = 1;i <= n;i++) scanf("%d",&a[i]);

    for(int i = 1;i <= n;i++) s[i] = s[i - 1] + a[i];
    while(m -- )
    {
        int l, r;
        scanf("%d%d",&l,&r);
        printf("%d\n",s[r] - s[l - 1]);

    }
    return 0;
}
AcWing 796.子矩阵的和
二维前缀和 
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
1. S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
2. s[i, j] = s[i - 1,j] + s[i,j - 1] - s[i - 1][j - 1] + a[i][j];
#include<iostream>
#include<cstdio>
using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N],s[N][N];

int main()
{
    scanf("%d%d%d",&n,&m,&q);

    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            scanf("%d",&a[i][j]);

    //初始化前缀和数组
    for(int i = 1;i <= n;i ++)
        for(int j = 1;j <= m;j ++)
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];

    //询问
    while(q -- )
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]);

    }

    return 0;
}

AcWing 797.差分
一维差分 
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

a[n] = b[1] + b[2] + ... + b[n] //a[i]是b[i]的前缀和
#include<iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N],b[N];

void insert(int l,int r,int c)
{
    b[l] += c;
    b[r + 1] -= c;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
    
    for(int i = 1;i <= n;i++) insert(i,i,a[i]);
    while(m -- )
    {
        int l, r, c;
        scanf("%d%d%d",&l,&r,&c);
        insert(l, r, c);
    }
    
    for(int i = 1;i <= n;i++) b[i] += b[i - 1];
    
    for(int i = 1;i <= n;i++) printf("%d ",b[i]);
    return 0;
}
AcWing 798.差分矩阵
二维差分 —— 模板题 AcWing 798. 差分矩阵
给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
a[i][j]是b[i][j]数组的前缀和
#include<iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N],b[N][N];

void insert(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            scanf("%d",&a[i][j]);
            
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            insert(i,j,i,j,a[i][j]);
    
    
    while(q--)
    {
        int x1,y1,x2,y2,c;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
        insert(x1,y1,x2,y2,c);
    }
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1]
    
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j ++) printf("%d ",b[i][j]);
        puts("");
    }
    return 0;
}

双指针算法

AcWing 799.最长连续不重复子序列
双指针算法
for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;

    // 具体问题的逻辑
    O(n^2)-->O(n)
}
常见问题分类:
    (1) 对于一个序列,用两个指针维护一段区间
    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
//Q:输出一个空格分割的字符串,每个一行
#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;

int main()
{
    char str[1000];

    gets(str);
    int n = strlen(str);

    for(int i = 0;i < n;i++)
    {
        int j = i;
        while(j < n && str[j] != ' ') j++;
        for(int k = i;k < j;k++) cout << str[k];
        cout << endl;

        i = j;
    }
    return 0;
}
//朴素 O(n^2)
for(int i = 0;i < n;i++)
    for(int j = 0;j < n;j++)
    if(check(j,i))
    {
        res = max(res,i- j + 1);
    }    
//双指针 O(n)
for(int i = 0,j = 0;i < n;i++)
{
    while(j <= i && check(j,i)) j ++;//j往左走最远能到什么地方
    res = max(res, i- j + 1);
}
#include<iostream>

using namespace std;

const int N = 100010;

int n;
int a[N],s[N];

int main()
{
    cin >> n;
    for(int i = 0;i < n;i++) cin >> a[i];

    int res = 0;
    for(int i = 0,j = 0;i < n;i++)
    {
        s[a[i]]++;
        while(s[a[i]] > 1)
        {
            s[a[j]]--;
            j++;
        }

        res = max(res,i - j + 1);
    }
    cout << res << endl;
    return 0;
}

AcWing 800.数组元素的目标和
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

const int N = 100010;

int n,m,x;
int a[N],b[N];

int main()
{
    scanf("%d%d%d",&n,&m,&x);
    for(int i = 0;i < n;i++) scanf("%d",&a[i]);
    for(int i = 0;i < m;i++) scanf("%d",&b[i]);

    for(int i = 0,j = m - 1;i < n;i++)
    {
        while(j >= 0 && a[i] + b[j] > x) j --;
        if(a[i] + b[j] == x)
        {
            printf("%d %d\n",i,j);
            break;
        }
    }

    return 0;
}

AcWing 2186.判断子序列
#include<iostream>
#include<cstring>

using namespace std;

const int N = 100010;

int n, m;
int a[N], b[N];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 0;i < n;i++) scanf("%d",&a[i]);
    for(int i = 0;i < m;i++) scanf("%d",&b[i]);
    
    int i = 0, j = 0;
    while(i < n && j < m)
    {
        if(a[i] == b[j]) i++;
        j ++ ;
        
    }
    if( i == n ) puts("Yes");
    else puts("No");
    return 0;
}

位运算

AcWing 801.二进制中1的个数
看n的二进制表示中第k位是几
1.先把第k位移到最后一位n >> k
2.看一下个位是几 x & 1
====>n >> k & 1(求第k位数字)
#include<iostream>

using namespace std;

int main()
{
    int n = 10;
    for(int k = 3;k>= 0; k--) cout << (n >> k & 1);
    return 0;
}
//lowbit(x):返回的是x的最后一位1
x = 1010 ===>10;
x = 101000 ===> 1000;

x & -x = x & (~x + 1)
x        = 101000100
~x       = 010111011
~x + 1   = 010111100
x&(~x+1) = 000000100
#include<iostream>
using namespace std;

int lowbit(int x)
{
    return x & ~x;
}

int main()
{
    int n; cin >> n;
    while(n -- )
    {
        int x; cin >> x;

        int res = 0;
        while(x) x -= lowbit(x),res ++;
        cout << res << ' ';
    }
    return 0;
}

	x = 1010
原码:0......01010
反码:1......10101
补码:1......10110

离散化

AcWing 802. 区间和
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // 映射到1, 2, ...n
}

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

using namespace std;

typedef pair<int, int> PII;

const int N = 300010;

int n, m;
int a[N], s[N];

vector<int>alls;
vector<PII>add, query;

int find(int x)
{
	int l = 0, r = alls.size() - 1;
	while (l < r)
	{
		int mid = l + r >> 1;
		if (alls[mid] >= x) r = mid;
		else l = mid + 1;
	}
	return r + 1;//下标从1...n
}
//vector<int >::iterator unique(vector<int>& a)
//{
//	int j = 0;
//	for (int i = 0; i < a.size(); i++)
//		if (!i || a[i] != a[i - 1])
//			a[j++] = a[i];
//	//a[0] ~ a[j - 1]所有a中不重复的数
//	return a.begin() + j;
//}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; i++)
	{
		int x, c;
		scanf("%d%d", &x, &c);
		add.push_back({ x,c });

		alls.push_back(x);
	}
	for (int i = 0; i < m; i++)
	{
		int l, r;
		scanf("%d%d", &l, &r);
		query.push_back({ l, r });

		alls.push_back(l);
		alls.push_back(r);
	}

	//去重
	sort(alls.begin(), alls.end());
	alls.erase(unique(alls.begin(), alls.end()), alls.end());

	//处理插入
	for (auto item : add)
	{
		int x = find(item.first);
		a[x] += item.second;
	}

	//预处理前缀和
	int len = alls.size();
	for (int i = 1; i <= len; i++) s[i] = s[i - 1] + a[i];

	//处理询问
	for (auto item : query)
	{
		int l = find(item.first), r = find(item.second);
		printf("%d\n", s[r] - s[l - 1]);
	}
	return 0;
}

区间合并

AcWing 803. 区间合并
// 将所有存在交集的区间合并
void merge(vector<PII> &segs)
{
    vector<PII> res;

    sort(segs.begin(), segs.end());

    int st = -2e9, ed = -2e9;
    for (auto seg : segs)
        if (ed < seg.first)
        {
            if (st != -2e9) res.push_back({st, ed});
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed, seg.second);

    if (st != -2e9) res.push_back({st, ed});

    segs = res;
}
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

typedef pair<int, int> PII;

const int N = 100010;

int n;
vector<PII>segs;

void merge(vector<PII>& segs)
{
	vector<PII> res;

	sort(segs.begin(), segs.end());

	int st = -2e9, ed = -2e9;
	for (auto seg : segs)
		if (ed < seg.first)
		{
			if (st != -2e9) res.push_back({ st,ed });
			st = seg.first, ed = seg.second;
		}
		else ed = max(ed, seg.second);
	if (st != -2e9) res.push_back({ st,ed });

	segs = res;
}
int main()
{
	cin >> n;

	for (int i = 0; i < n; i++)
	{
		int l, r;
		cin >> l >> r;
		segs.push_back({ l,r });
	}

	merge(segs);

	cout << segs.size() << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

噶米困了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值