栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

前缀和与差分

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

前缀和与差分

前缀和与差分

文章目录

前缀和与差分

1、二维差分2、二维差分入门练习—P3397 地毯3、一维差分练习—P3406 海底高铁4、差分思想优化—序列查询新解(CSP杂题)reference

1、二维差分

一维差分时是在需要更新的位置开始,不需要的位置结束,而二维的也一样。如果我们要在左上角是 (x1, y1),右下角是(x2,y2)的矩形区间每个值都+a,我们要在区间开始位置(x1,y1)处+a,根据前缀和的性质,那么它影响的就是整个黄色部分,多影响了两个蓝色部分,所以在两个蓝色部分-a消除+a的影响,而两个蓝色部分重叠的绿色部分多受了个-a的影响,所以绿色部分+a消除影响。

2、二维差分入门练习—P3397 地毯

P3397 地毯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

暴力可以做,复杂度n3勉强满足n,m <= 1000,使用二维前缀和和二维差分后,复杂度降至O(n2)。差分与前缀和互为逆运算,一维差分使用O(1)复杂度表示O(n)的覆盖,而二维差分使用O(1)的复杂度表示O(n2)的覆盖

#include
#include
using namespace std;
long long m,n,a[1010][1010];

//直接模拟
void solve1(){
    cin>>n>>m;
    long long x1,y1,x2,y2;
    for(long long i=0;i>x1>>y1>>x2>>y2;
        for(long long j=x1;j<=x2;j++){
            for(long long k=y1;k<=y2;k++) a[j][k]++;
        }
    }
    for(long long i=1;i<=n;i++){
        for(long long j=1;j<=n;j++) printf("%lld ",a[i][j]);
        printf("n");
    }
}

//二维差分
void solve2(){
    cin>>n>>m;
    long long x1,x2,y1,y2;
    for(long long i=1;i<=m;i++){
        cin>>x1>>y1>>x2>>y2;
        a[x1][y1]++;
        a[x1][y2+1]--;
        a[x2+1][y1]--;
        a[x2+1][y2+1]++;
    }
    //逆向还原(求前缀和)
    for(long long i=1;i<=n;i++){
        for(long long j=1;j<=n;j++){
            a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
            printf("%lld ",a[i][j]);
        } 
        printf("n");
    }
}

int main()
{
    solve2();
    system("pause");
    return 0;
}
3、一维差分练习—P3406 海底高铁

P3406 海底高铁 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题是一维前缀和/一维差分,采用差分思想O(m)读入,使用前缀和获取覆盖并进行方案选择花费O(n)。总的时间复杂度O(m+n)可以把前缀和与选择方案合二为一

#include 
#include 
using namespace std;

long long n,m,A,B,C,cover[100100];
long long dist=0;


void solve1(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	long long startx, endx;
	cin >> startx;
	//一维差分输入
	for(long long j = 2; j <= m; j++){
		scanf("%lld",&endx);
		if(startx < endx){
			cover[startx]++;
			cover[endx]--;
		}
		else{
			cover[endx]++;
			cover[startx]--;
		}
		//Swap(startx,endx);
		startx = endx;
	}
	//前缀和求覆盖+选择
	for(long long i = 1; i < n; i++){
        cover[i] += cover[i - 1];
		scanf("%lld%lld%lld",&A,&B,&C);
		dist += A*cover[i] < B*cover[i] + C ? A*cover[i] : B*cover[i] + C;
	}
}

int main()
{
	solve1();
    //system("pause");
    cout< 

教训:

1、long long和int 不要混用,或许会带来精度的损失导致出错?这道题混用的记录如下记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

2、理解清楚题意再做,有些情况下没有AC并不是算法问题,可能是题目中的某一小步理解错误,比如这道题“从城市 P1出发分别按照P1,P2,P3…PM 的顺序访问各个城市”被我理解成“从城市1出发…”导致与最终结果总相差一个常数

3、长时间WR看答案后认为算法没问题就要仔细再读题,仔细看题解找不同。

4、差分思想优化—序列查询新解(CSP杂题)

计算机软件能力认证考试系统

注意到N在109量级,而n在105直接模拟会超时得到70分

思想:将0—N的模拟转化为0—n的等差数列求和。具体是按照这n个数进行分组,然后按照r个等差数列求和,最后减去多求的部分(代码中error -= fabs(first) * A_begin_rem + fabs(last) *A_end_rem)

#include 
#include 
using namespace std;

long long func(long long first, long long last, long long r)
{
	long long result;
	if (first * last >= 0)
	{
		result=fabs(first + last) * (fabs(last - first) + 1) / 2 * r;
	}
	else
	{
		result = (fabs(first) + 1) * fabs(first) / 2 * r + (last + 1) * last / 2 * r;
	}
	return result;
}

int main()
{

	int n, N;
	cin >> n >> N;
	long long r = N / (n + 1);

	long long A[n]={0};
	for (int i = 1; i <= n; ++i)
	{
		cin >> A[i];
	}
    A[n+1]=N;

	long long error = 0;
	for (int i = 0; i <= n; ++i)
	{
		long long begin_num = A[i] / r;
		long long end_num = (A[i + 1] - 1) / r;

		long long first = begin_num - i, last = end_num - i;

		error += func(first, last, r);

		long long A_bebin_rem = A[i] % r;
		long long A_end_rem = r - (A[i + 1] - 1) % r - 1;

		error -= fabs(first) * A_begin_rem + fabs(last) * A_end_rem;
	}

	cout << error << endl;
    //system("pause");
	return 0;
}

教训:

1、中间变量命名规范,便于阅读

2、写程序之前可以先在纸上演算,确定思路正确再写代码

3、适当空格、空行使代码看起来清晰

reference

二维差分 - 新之守护者 - 博客园 (cnblogs.com)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/743455.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号