洛谷 P2801 教主的魔法 題解
此文為博主原創題解,轉載時請通知博主,並把原文鏈接放在正文醒目位置。
題目鏈接:https://www.luogu.org/problem/show?pid=2801
題目描述
教主最近學會了一種神奇的魔法,能夠使人長高。於是他準備演示給XMYZ信息組每個英雄看。於是N個英雄們又一次聚集在了一起,這次他們排成了一列,被編號為1、2、……、N。
每個人的身高一開始都是不超過1000的正整數。教主的魔法每次可以把閉區間[L, R](1≤L≤R≤N)內的英雄的身高全部加上一個整數W。(雖然L=R時並不符合區間的書寫規範,但我們可以認為是單獨增加第L(R)個英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,於是他們有時候會問WD閉區間 [L, R] 內有多少英雄身高大於等於C,以驗證教主的魔法是否真的有效。
WD巨懶,於是他把這個回答的任務交給了你。
輸入輸出格式
輸入格式:第1行為兩個整數N、Q。Q為問題數與教主的施法數總和。
第2行有N個正整數,第i個數代表第i個英雄的身高。
第3到第Q+2行每行有一個操作:
(1) 若第一個字母為“M”,則緊接著有三個數字L、R、W。表示對閉區間 [L, R] 內所有英雄的身高加上W。
(2) 若第一個字母為“A”,則緊接著有三個數字L、R、C。詢問閉區間 [L, R] 內有多少英雄的身高大於等於C。
對每個“A”詢問輸出一行,僅含一個整數,表示閉區間 [L, R] 內身高大於等於C的英雄數。
輸入輸出樣例
輸入樣例#1:5 3 1 2 3 4 5 A 1 5 4 M 3 5 1 A 1 5 4輸出樣例#1:
2 3
說明
【輸入輸出樣例說明】
原先5個英雄身高為1、2、3、4、5,此時[1, 5]間有2個英雄的身高大於等於4。教主施法後變為1、2、4、5、6,此時[1, 5]間有3個英雄的身高大於等於4。
【數據範圍】
對30%的數據,N≤1000,Q≤1000。
對100%的數據,N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000。
分析:
題目看起來很簡單,但是一百萬的數據量,暴力寫的話絕對超時。
TLE怎麽辦?區間修改詢問用分塊~
AC代碼:.(感謝@WZY大佬提供題解程序供博主學習...)
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 inline void read(int &x) 6 {//快速讀入 7 char ch = getchar();char c;x = 0; 8 while(ch >‘9‘ || ch < ‘0‘) c = ch,ch = getchar(); 9 while(ch >= ‘0‘ && ch <= ‘9‘) x = x*10 +ch-‘0‘,ch = getchar(); 10 if(c == ‘-‘) x = -x; 11 } 12 inline void put(int x) 13 {//快速輸出 14 if (x < 0)x = ~x + 1, putchar(‘-‘); 15 if (x > 9) put(x / 10);putchar(x % 10 + 48); 16 } 17 const int MAXN = 1000008; 18 const int SN = 1008; 19 int num[MAXN],s[MAXN],belong[MAXN],flag[MAXN],block,size; 20 // 原始··· 塊內·· 歸屬···· 加法標記·· 塊數 ·每塊的大小 21 int L[MAXN],R[MAXN]; 22 //L[i]:塊i的左端點下標 R[i]:塊i的右端點下標 23 24 int res(int x) 25 {//對第x個塊進行排序 26 int l = L[x],r = R[x]; 27 for(register int i = l;i <= r;i ++) 28 s[i] = num[i]; 29 std::sort(s+l,s+r+1); 30 } 31 32 int find(int x,int k) 33 {//二分查找第x個塊,並找出大於k的部分 34 int l = L[x],r = R[x]; 35 int last = r; 36 int mid; 37 while(l <= r) 38 { 39 mid = (l + r) >> 1; 40 if(s[mid] < k) l = mid + 1; 41 else r = mid - 1; 42 } 43 return last - l + 1; 44 } 45 46 inline void modify(int l,int r,int k) 47 {//區間修改 48 if(belong[l] == belong[r]) 49 { 50 for(register int i = l;i <= r;i ++) 51 num[i] = num[i] + k; 52 res(belong[l]); 53 return; 54 } 55 56 register int ll = belong[l],rr = belong[r]; 57 if(L[ll] < l) 58 { 59 for(register int i = l;i <= R[ll];i ++) 60 num[i] += k; 61 res(belong[l]); 62 ++ ll; 63 } 64 if(R[rr] > r) 65 { 66 for(register int i = L[rr];i <= r;i ++) 67 num[i] += k; 68 res(belong[r]); 69 -- rr; 70 } 71 for(register int i = ll;i <= rr;++ i) 72 flag[i] += k; 73 } 74 75 int ask(int l,int r,int k) 76 {//區間詢問 77 register int ans = 0; 78 if(belong[l] == belong[r]) 79 { 80 for(int i = l;i <=r;++ i) 81 if(num[i] + flag[belong[i]] >= k) 82 ans ++; 83 return ans; 84 } 85 register int ll = belong[l],rr = belong[r]; 86 if(L[ll] < l) 87 { 88 for(register int i = ll;i <= R[ll];i ++) 89 if(num[i] + flag[belong[i]] >= k) 90 ans ++; 91 ++ ll; 92 } 93 if(R[rr] > r) 94 { 95 for(register int i = L[rr];i <= r;++ i) 96 if(num[i] + flag[belong[i]] >= k) 97 ans ++; 98 -- rr; 99 } 100 for(register int i = ll;i <= rr;++ i) 101 ans = ans + find(i,k - flag[i]); 102 return ans; 103 } 104 int main() 105 { 106 int n,q; 107 read(n),read(q); 108 register char c; 109 register int l,r,x; 110 /*------------------------------------------------*/ 111 //初始化各個塊 112 block = sqrt(n); 113 if(n % block) size = n/block + 1; 114 else size = n/block; 115 116 for(register int i = 1;i <= n;++ i) 117 { 118 read(num[i]); 119 belong[i] = (i - 1)/block + 1; 120 s[i] = num[i]; 121 } 122 for(register int i = 1;i <= size;++ i) 123 { 124 L[i] = (i -1) * block + 1; 125 R[i] = i * block; 126 } 127 if(R[size] > n) R[size] = n; 128 for(register int i = 1;i <= size;++ i) 129 res(i); 130 /*------------------------------------------------*/ 131 //處理修改和詢問操作 132 for(register int i = 1;i <= q; ++ i) 133 { 134 c = getchar(); 135 while(c < 30) c = getchar(); 136 read(l),read(r),read(x); 137 if(c == ‘A‘) 138 put(ask(l,r,x)),putchar(‘\n‘); 139 else 140 modify(l,r,x); 141 } 142 return 0; 143 }
洛谷這道題的數據比較弱,所以容易AC
洛谷 P2801 教主的魔法 題解