發表文章

目前顯示的是有「BIT」標籤的文章

[TIOJ] 1941. 直升機抓寶 (Helicopter)

題目連結: https://tioj.infor.org/problems/1941 這題應該可以輕鬆想到$O(N^2)$的DP方式,同時也會發現那樣子太慢。 仔細觀察DP的表格後,發現他是一個單調的表格,也就是由左至右遞增,同時由下至上遞增,接著研究一下每個轉移對DP表格的貢獻,發現他必定會將他自己那個區間的DP值+1,而後面則為了要維護單調的性質,會再跟他取max。 那這類題目,其實我們可以維護每一段的DP值,每次加入一個新的轉移來源後,也代表加入了一個斷點,但是同時也有可能把自身後面的斷點吃光光。 所以複雜度會是$O(N log N)$,因為每個斷點只會出現跟消失一次,而單次新增跟刪除了複雜度都是$O(log N)$ #include <bits/stdc++.h> using namespace std; class BIT{ private: int n; vector<int> arr; inline int lowbit(int x){return x&(-x);} void modify(int p, int x){ for(;p;p-=lowbit(p)) arr[p] += x; } public: void init(int n_){ n = n_; arr.clear(); arr.resize(n); } void modify(int l, int r, int v){ modify(l, -v); modify(r, v); } int query(int x){ int ret = 0; for(;x<n;x+=lowbit(x)) ret += arr[x]; ...

[TIOJ] 1852. 分眼皮

題目連結: http://tioj.infor.org/problems/1852 有趣的題目XD,看到24先猜砍半枚舉,但想了一段時間才知道該怎麼讓枚舉出來的兩個集合跟答案有關連。 首先發現,假設最後選出來的三個數字分別是$(a, b, c)$(不失一般性假設$a \geq b \geq c$),也就是說假設在枚舉出來的第一個集合中有一個三元組$(x, y, z)$(沒有大小關係),那只要在第二個集合中找到$(x^\prime, y^\prime, z^\prime)$使得$x+x^\prime \geq y+y^\prime \geq z+z^\prime$就好。 接著考慮$a-b, c-b$,發現只要$a-b\geq 0 \land c-b \leq 0$就可以代表$a \geq b \geq c$,也就是說對於第一個集合中的$(x, y, z)$,我們就是要找到使得$x+x^\prime-(y+y^\prime) \geq 0 \land z+z^\prime - (y+y^\prime) \leq 0$(也就是$y^\prime - x^\prime \leq x-y \land y^\prime - z^\prime \geq z-y$)成立的$(x^\prime, y^\prime, z^\prime)$中,$x^\prime - z^\prime$最小的那個,因為他就代表$(x, y, z)$可以配出的最小值($x+x^\prime - (z+z^\prime)$)。 這樣,就會發現問題轉換成給你一堆二維帶權資料點($(y^\prime - x^\prime, y^\prime - z^\prime)$, $x^\prime - z^\prime$),請求出第一維大於等於某個數且第二維小於等於某個數中,值最小的那個。這問題簡單的對一維排序後,加上一些前(後)綴極值資料結構,再利用雙指針就可以解決了 #include <bits/stdc++.h> using namespace std; typedef int64_t lld; const int N = 24; const lld INF = 1LL<<60; class BIT{ private:...

[TIOJ] 1134. 1.蓋房子問題

題目連結: http://tioj.infor.org/problems/1134 想了想覺得把1換成-1, 0換成1之後會有好事情,然後似乎可以做類似最大子矩陣的方式,但是我不知道該怎麼用。 被雷了之後才發現其實我只要把兩個問題合在一起就好了,一個是最大子矩陣,一個是某種找有多少大於零的序列的問題。首先我們可以先對一維用前綴和之後枚舉頂跟底,接著就變成一維的問題了,變成一維的問題後就是要問說對於所有大於零的序列中,最長是多少,作法也是考慮枚舉前綴和,每次只要查小於當前前綴和的所有鍵值中最小的值是多少,接著就把當前前綴和當做鍵值,現在的位置當作值插到某種資料結構就好了。 #include <bits/stdc++.h> using namespace std; #define ALL(x) begin(x), end(x) const int N = 200 + 5; const int INF = 1<<30; class LiSan{ private: vector<int> vv; public: inline void init(){vv.clear();} inline void insert(int x){vv.push_back(x);} inline void done(){ sort(ALL(vv)); vv.resize(distance(vv.begin(), unique(ALL(vv)))); } inline int size(){return vv.size();} inline int get(int x){ return distance(vv.begin(), lower_bound(ALL(vv), x)); } inline int inv_get(int x){return vv[x];} ...

[Codeforces] 198E. Gripping Story

題目連結: http://codeforces.com/contest/198/problem/E 被雷了才會QQ 會發現其實他是個圓並不重要,重要的其實是距離,所以不妨把每個人的座標轉換成跟原點的距離,這樣的話每次詢問就變成詢問一個距離內質量小於k的數有誰,而我們其實也不用一次把一坨東西拉出來,只要一次拉一個就好,反正最多拉n個,複雜度不會太慘。而這樣其實就是詢問一個前綴極值就可達到這件事,所以就開個BIT套個單調的queue之類的就可以做到這件事了。 #include <bits/stdc++.h> using namespace std; typedef long long lld; #define PB push_back #define ALL(x) begin(x), end(x) #define FF first #define SS second const int N = 250000 + 5; const lld INF = 1LL<<31; class LiSan{ private: vector<lld> vv; public: inline void init(){vv.clear();} inline void insert(lld x){vv.PB(x);} inline void done(){ sort(ALL(vv)); vv.resize(distance(vv.begin(), unique(ALL(vv)))); } inline int size(){return vv.size();} inline int get(lld x){ return distance(vv.begin(), lower_bound(ALL(vv), x)); } inline lld ...

[NTUJ] 0903. Shadow Hunters

題目連結: http://acm.csie.org/ntujudge/problem.php?id=903 有趣的好題,想了很久只想的到樹鍊剖分的作法,被雷了才知道更好的做法QQ 對於全子樹加一其實很簡單,就把樹壓扁後把$[in[x], out[x]]$全部加一就好,然後詢問時則單點詢問邊即可。但是另外兩個操作就會變得很難做,所以要用另外一種方式想,考慮一條邊若或被加值,則一定是因為它下面的點有被加值,所以其實對最底下的點加值即可,然後為了怕太上面的人也會被算到,記得超過最頂之後要再減一以免多算,這樣就變成單點加值區間查詢了。 #include <bits/stdc++.h> using namespace std; #define PB push_back const int N = 100000 + 5; class BIT1{ #define lowbit(x) ((x)&(-(x))) private: int arr[N], size; inline int query(int x){ int r=0; while(x){ r+=arr[x]; x-=lowbit(x); } return r; } public: inline void init(int x){ fill(arr, arr+size, 0); size=x; } inline int query(int l, int r){ // 1-base (l, r] return query(r)-query(l); } inline void add(int x,...

[TIOJ] 1272. The Agency

題目連結: http://tioj.infor.org/problems/1272 給定一棵樹,然後要求詢問某個節點的值是偶數或奇數,或者對一整顆子樹加一。不妨把樹壓平後,透過進入時間戳與出去的時間戳,得到一個區間$[in[x], out[x])$,而其實這個區間就是x的子樹,所以發現要對子樹加值就是對那個區間加值,而查詢則是查$in[x]$。把問題轉成單點查詢區間加值後,其實一個BIT就可以做到了。 #include <bits/stdc++.h> using namespace std; #define PB push_back const int N = 100000 + 5; class BIT{ private: int arr[N], size=0; inline int lowbit(int x){ return x&-x; } public: void init(int s){ fill(arr, arr+s, 0); size=s; } //1-base (l, r] void add(int l, int r, int v){ add(l, -v); add(r, v); } void add(int s, int v){ while(s){ arr[s]+=v; s-=lowbit(s); } } int query(int x){ int r=0; while(x<size){ ...

[AtCoder] ARC 077 E: guruguru

題目連結: http://arc077.contest.atcoder.jp/tasks/arc077_c 賽中完全沒頭緒,最後是被雷了才知道這題怎麼做的XD想法大概是我們在算cost的時候其實就是算$\sum_{i=1}^{n}(R_i-L_i)$,那顯然等價於$\sum_{i=1}^{n}R_i-\sum_{i=1}^{n}L_i$(注意若其中$R_i #include <bits/stdc++.h> using namespace std; typedef long long lld; const int INF = 2000000000; const int N = 100000 + 10; lld arr[N], cnt[N], sm[N]; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n, m;cin>>n>>m; for(int i=0;i<n;i++) cin>>arr[i]; lld sumL=0, sumR=0; for(int i=0;i<n-1;i++) sumL+=arr[i]; for(int i=1;i<n;i++) sumR+=arr[i]+m*(arr[i-1]>arr[i]); for(int i=1;i<n;i++){ if(arr[i-1]>arr[i]){ cnt[arr[i-1]+1]+=1; cnt[m+1]-=1; cnt[1]+=1; cnt[arr[i]+1]-=1; sm[arr[i-1]+1]+=arr[i-1]; sm[m+1]-=arr[i-1]; sm[1]+=arr[i-1]-m; sm[arr...

[TIOJ] 1493. 三個農夫

題目連結: http://tioj.infor.org/problems/1493 本題要求在樹上對一個節點單點加值或是問一個節點及其子樹的和,顯然直接照著做會TLE,所以不妨考慮把樹壓扁後的序列,修改一個節點的值就是對序列作單點修改,問子樹的話也會剛好是一個連續的區間,所以就直接作區間詢問就好。單點修改,區間查詢就是一個BIT支援的操作,所以就開三個BIT把三種果實維護好即可。 #include <bits/stdc++.h> using namespace std; #define PB push_back typedef pair<int, int> PII; #define FF first #define SS second const int N = 1000000 + 5; vector<int> g[N]; int st[N], ed[N], timer=1; void dfs(int,int); struct BIT{ int arr[2*N]; inline int lowbit(int x){ return x&(-x); } void add(int w, int c){ while(w<=timer){ arr[w]+=c; w+=lowbit(w); } } int query(int l, int r){ return query(r-1) - query(l-1); } int query(int w){ int r=0; while(w){ r+=arr[w]; w-=lowbit(w); } return r;...

[TIOJ] 1869. 堆石子遊戲

題目連結: http://tioj.infor.org/problems/1869 本題我一開始以為可以開個N棵線段樹之類的,結果我就TLE了QAQ,後來想想才發現N棵線段樹的話,複雜度是$O(Q N \log N)$難怪會TLE,想了想發現似乎可以樹套樹(二維BIT),畢竟他只有區間和跟單點修改,那就直接做吧XD,複雜度$O(Q \log ^2 N)$ #include <bits/stdc++.h> using namespace std; #define N 1024 #define lowbit(x) (x)&(-x) int BIT[N+10][N+10]={0}; int n; void edit(int,int,int); int query(int,int); int main(){ scanf("%d",&n); int type; while(scanf("%d",&type)!=EOF){ if(type==1){ int x,y,z; scanf("%d%d%d",&x,&y,&z); x++,y++; edit(x,y,z); }else{ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); x1++,y1++,x2++,y2++; int s = query(x2,y2) + query(x1-1,y1-1) - query(x1-1,y2) - query(x2,y1-1); printf("%d\n",s); ...

[TIOJ] 1228. Phh多層次傳銷公司

題目連結: http://tioj.infor.org/problems/1228 本題是建中資訊校內複賽題,當時我在寫的時候完全不會線段樹甚麼的,所以我那時是直接照著做,然後稍微二分搜一下他的區間,就拿到了第二組subtask,因此比完之後我一直以為這題很簡單,直到聽完題解說要把樹壓扁、開BIT,我才發現原來沒有想像中的簡單。 --以上廢話-- 本題把樹壓扁後就變成單點修改區間查詢的序列問題了,但什麼是把樹壓扁呢,在本題中可以發現我們會修改以及查詢的其實都是邊權,所以其實可以用一定的方法幫這些邊編號,而因為我們在查區間和時是用DFS的方法去跑,所以不妨就用DFS時碰到邊的順序幫它編號,但記得要開個mapping能讓它之後詢問跟修改時知道要對幾號做動作,大概就醬吧。 p.s本題記憶體有點卡,不能寫線段樹,只能寫BIT(至少我就醬被梗了一段時間 #include <bits/stdc++.h> using namespace std; #define N 1000000 #define lowbit(x) ((x)&-(x)) struct node{ int child,val; }; struct dian{ int l,r; }; vector<node> tree[N+5]; dian dianMap[N+5]; int bianMap[N+5]; int BIT[N+5]; int n,q; int counter=0; int arr[N]; void foldTree(int); void buildBIT(); void edit(int,int); inline int sum(int); int query(int,int); int main(){ scanf("%d%d",&n,&q); for(int i=0;i<n-1;i++){ int a,b,m; scanf("%d%d...