1. 程式人生 > >#6280. 數列分塊入門 4 #6281. 數列分塊入門 5

#6280. 數列分塊入門 4 #6281. 數列分塊入門 5

輸出 pos code 完整 計算 i++ const type pan

題目描述

給出一個長為 n 的數列,以及 n 個操作,操作涉及區間加法,區間求和。

輸入格式

第一行輸入一個數字 n

第二行輸入 n 個數字,第 i 個數字為 ai?,以空格隔開。

接下來輸入 n 行詢問,每行輸入四個數字 opt、l、r、c,以空格隔開。

opt=0,表示將位於 [l,r]的之間的數字都加 c

opt=1,表示詢問位於 [l,r]的所有數字的和 ?mod(c+1)。

輸出格式

對於每次詢問,輸出一行一個數字表示答案。

樣例

樣例輸入

4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4

樣例輸出

1
4
思路:分塊,不是整塊的暴力加,整塊的用sum[]數組維護整塊的和。
代碼:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=5e4+10;
ll a[maxn],b[maxn],sum[maxn],pos[maxn],n,block;
ll ans;
void update(int l,int r,int c)//更新 { for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左邊不完整的塊暴力加,sum數組維護 { sum[pos[i]]+=c; a[i]+=c; } if(pos[l]!=pos[r]) { for(int i=(pos[r]-1)*block+1;i<=r;i++)//右邊不完整的塊暴力加,sum數組維護 { sum[pos[i]]
+=c; a[i]+=c; } } for(int i=pos[l]+1;i<=pos[r]-1;i++)//整塊的b數組保存 b[i]+=c; } ll query(int l,int r,int mod)//查詢 { ans=0; for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左邊不完整的塊,暴力加每個數 ans+=a[i]+b[pos[l]]; if(pos[l]!=pos[r]) { for(int i=(pos[r]-1)*block+1;i<=r;i++)//右邊不完整的塊,暴力加每個數 ans+=a[i]+b[pos[r]]; } for(int i=pos[l]+1;i<=pos[r]-1;i++)//完整的塊整塊加 { ans+=sum[i]+b[i]*block; } return ans%mod; } int main() { scanf("%d",&n); memset(sum,0,sizeof(sum)); block=sqrt(n);//塊的大小 for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pos[i]=(i-1)/block+1; sum[pos[i]]+=a[i];//sum數組維護整塊的和 } int opt,l,r,c; for(int i=0;i<n;i++) { scanf("%d%d%d%d",&opt,&l,&r,&c); if(opt==0) update(l,r,c); else if(opt==1) printf("%lld\n",query(l,r,c+1)); } return 0; }

第5題:

思路:這裏有個小思維,因為數的大小小於2^31,最多開方5次就為1。所以我們做法和上面一樣有sum數組維護整塊的值,

計算整塊的時候,用flag記錄整組都為1的情況,如果全為1就加block(塊的大小),不是就暴力加,不會超時的,因為最多5次開方

就全為1了,時間復雜度和上面的一樣(5倍對時間復雜度沒關系)

代碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define inf 0x3f3f3f
using namespace std;
typedef long long ll;
const int maxn=5e4+10;
ll a[maxn],pos[maxn],sum[maxn],flag[maxn],block,n;
//a數組存儲數字,pos記錄每個數在哪個塊,sum維護整塊的和,flag標記整塊是否全為1,blocK塊的大小。 
void update(int l,int r)//更新 
{
    for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左邊不完整塊,暴力加。 
    { 
        sum[pos[l]]-=a[i];
        a[i]=sqrt(a[i]);
        sum[pos[l]]+=a[i];//sum數組記得改動 
    }
    if(pos[l]!=pos[r])//右邊不完整塊,暴力加。
    {
        for(int i=(pos[r]-1)*block+1;i<=r;i++)
        {
            sum[pos[r]]-=a[i];
            a[i]=sqrt(a[i]);
            sum[pos[r]]+=a[i];
        }
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)//整塊 
    {
        if(flag[i])//全為1,直接跳過 
            continue;
        else//不是全為1,暴力處理 
        {
            flag[i]=1;//先假設全為1,後面如果不是改為0 
            for(int j=(i-1)*block+1;j<=i*block;j++)
            {
                sum[i]-=a[j];
                a[j]=sqrt(a[j]);
                sum[i]+=a[j];
                if(a[j]!=1)//不是全為1 
                    flag[i]=0;
            }
        }
    }
}

void solve(int l,int r)
{
    ll ans=0;
    for(int i=l;i<=min(pos[l]*block,(ll)r);i++)//左邊不完整塊,暴力加 
        ans+=a[i];
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*block+1;i<=r;i++)//右邊不完整塊,暴力加 
            ans+=a[i];
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)//完整塊直接加,整塊的和即可 
        ans+=sum[i];
    printf("%lld\n",ans);
}
int main()
{
    scanf("%d",&n);
    block=sqrt(n);//塊的大小 
    memset(flag,0,sizeof(flag));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        pos[i]=(i-1)/block+1;
        sum[pos[i]]+=a[i];//每塊的和 
    }
    int opt,l,r,c;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",&opt,&l,&r,&c);
        if(opt==0)
            update(l,r);
        else
            solve(l,r);
    }
    return 0;
}

#6280. 數列分塊入門 4 #6281. 數列分塊入門 5