1. 程式人生 > >洛谷 P2801 教主的魔法 題解

洛谷 P2801 教主的魔法 題解

數字 modify 表示 pre 二分查找 tchar inline 感謝 str

此文為博主原創題解,轉載時請通知博主,並把原文鏈接放在正文醒目位置。

題目鏈接: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 教主的魔法 題解