#6280. 數列分塊入門 4 #6281. 數列分塊入門 5
阿新 • • 發佈:2018-10-12
輸出 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