zoukankan      html  css  js  c++  java
  • 【POJ 3468】A Simple Problem with Integers【分块】

    题目大意:

    题目链接:http://poj.org/problem?id=3468
    给出一个初始数列,有两种操作:

    • Q x yQ\ x\ y,输出这个数列xxyy之间的数字和。
    • C x y zC\ x\ y\ z,将这个数列的xxyy之间的数加上zz

    思路:

    线段树方法
    树状数组方法

    这道题还可以用分块做。。。
    模板的分块,就不多解释了。
    把这个数列分成n\sqrt{n}块,每块长度不超过n\sqrt{n},每块有如下几个变量:

    • sum[x]sum[x]表示第xx块里面所有数的总和
    • add[x]add[x]表示将第xx块整体加或减时的值
    • L[x]L[x]表示第xx块的最左边的位置
    • R[x]R[x]表示第xx块的最右边的位置

    每一块都有一些关于数字的变量,那么每一个数字也有它的那一块的变量:

    • a[i]a[i]不用解释了。。。读入的初始值
    • pos[i]pos[i]数字ii属于第几块

    过程详见代码:


    代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <iostream>
    #define N 100011
    #define ll long long
    using namespace std;
    
    int n,m,L[N],R[N],pos[N],x,y,t;
    ll add[N],sum[N],a[N],z;
    char c;
    
    ll ask(int l,int r)  //查找
    {
    	int q=pos[l],p=pos[r];  //左右边界分别的块
    	ll ans=0;
    	if (q==p)  //在同一块就直接暴力求答案(反正不会超过sqrt(n))
    	{
    		for (int i=l;i<=r;i++)
    		 ans+=a[i]+add[q];  //a[i]是原来的答案,add[q]是整体加的时候这一块加的和。
    		return ans;
    	} 
    	//不在同一块,就将中间的包含了整块整块的直接求出来
    	//然后在将左右两边用上述暴力方法求出
    	for (int i=q+1;i<p;i++)
    	 ans+=add[i]*(ll)(R[i]-L[i]+1)+sum[i];  //中间整块的答案
    	for (int i=l;i<=R[q];i++)
    	 ans+=a[i]+add[q];  //暴力左边
    	for (int i=L[p];i<=r;i++)
    	 ans+=a[i]+add[p];  //暴力右边
    	return ans;
    }
    
    void Add(int l,int r,ll z) //修改
    {
    	int q=pos[l],p=pos[r];
    	if (q==p)  //在同一块就直接暴力
    	{
    		for (int i=l;i<=r;i++)
    		{
    			a[i]+=z;  //直接暴力加上
    			sum[q]+=z;  //这一块的总和也要加
    		}
    		return;
    	}
    	//还是像查找那样,先求出中间的整段的答案,两边在分开处理
    	for (int i=q+1;i<p;i++) 
    	 add[i]+=z;  //中间整段都加上z
    	for (int i=l;i<=R[q];i++)  //左边
    	{
    		a[i]+=z;
    		sum[q]+=z;
    	}
    	for (int i=L[p];i<=r;i++)  //右边
    	{
    		a[i]+=z;
    		sum[p]+=z;
    	}
    	return;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	 scanf("%lld",&a[i]);
    	t=(int)sqrt(n);  //总块数
    	for (int i=1;i<=t;i++)
    	{
    		L[i]=(i-1)*t+1;
    		R[i]=i*t;  //求出每一快的长度和左右边界
    		for (int j=L[i];j<=R[i];j++)
    		{
    			pos[j]=i;
    			sum[i]+=a[j];  //初始化
    		}
    	}
    	if (R[t]<n)  //有可能还会差一块,补上
    	{
    		L[++t]=R[t-1]+1;
    		R[t]=n;
    		for (int i=L[t];i<=R[t];i++)
    		{
    			pos[i]=t;
    			sum[t]+=a[i];
    		}
    	}
    	while (m--)
    	{
    		cin>>c;
    		scanf("%d%d",&x,&y);
    		if (c=='Q')
    		{
    			cout<<ask(x,y)<<endl;
    		}
    		else 
    		{
    			scanf("%lld",&z);
    			Add(x,y,z);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    kibana简单使用
    全文检索 高亮显示 数据库同步添加
    算法: Reverse Bits 反转位收藏
    算法:Number of 1 Bits 收藏
    JS创建二维数组
    查询状态在1,2,5,7的记录-oracle
    oracle 修改某字段,判断是否为空,如果为空赋默认值
    在运筹学中什么样的解决方案是最优的
    项目开发失败
    筛选符合条件的数据后,如何做数据源,绑定repeater
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998636.html
Copyright © 2011-2022 走看看