發表文章

目前顯示的是有「stack優化」標籤的文章

[TIOJ] 1721. 山上的風景

題目連結: http://tioj.infor.org/problems/1721 往右往左各分別維護一個遞減的stack,而若是塞入的東西比較大,則就把比他小的全部pop掉,同時也保證他們都只能看到這裡。 #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 = 100000 + 5; int arr[N], ans[N]; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; while(cin>>n){ for(int i=0;i<n;i++) cin>>arr[i]; stack<PII> ss; for(int i=0;i<n;i++){ while(!ss.empty() and ss.top().FF <= arr[i]){ ans[ss.top().SS] = i-ss.top().SS+1; ss.pop(); } ss.push({arr[i], i}); } while(!ss.empty()){ ans[ss.top().SS] = n-ss.top().SS; ss.pop(); } for(int i=n-1;i>=0;i--){ while(!ss.empty() and ss.top().FF ...

[TIOJ] 1637. 我愛台灣

題目連結: http://tioj.infor.org/problems/1637 枚舉每個點,看有哪些區間會以他為最大值,若是裸著做可能會有個$O(n^2)$的作法,但是其實會發現維護那些區間會以他為最大值,其實可以先找出最長且以他為最大值的區間,而要做這件事就只要用個stack維護一下左邊右邊第一個比自己大的數在哪就好(維護stack的遞減性就可做到)。維護好後算答案其實也不難,區間的可能個數就是右邊元素的數量乘以左邊元素的數量。 #include <bits/stdc++.h> using namespace std; typedef long long lld; typedef pair<lld, int> PLI; #define FF first #define SS second const int N = 1000000 + 5; lld arr[N], LL[N], RR[N]; vector<PLI> ss; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; cin>>n; for(int i=0;i<n-1;i++) cin>>arr[i]; for(int i=0;i<n-1;i++){ while(!ss.empty() and ss.back() < (PLI){arr[i], i}) ss.pop_back(); LL[i] = ss.empty()?-1:ss.back().SS; ss.push_back({arr[i], i}); } ss.clear(); for(int i=n-2;i>=0;i--){ while(!ss.empty() and ss.back() < (PLI){arr[i], i}) ss.pop_back(); RR[i] =...

[TIOJ] 1368. Get High!!

題目連結: http://tioj.infor.org/problems/1368 我這題想了好久好久,然後某天突然感覺這題可以這樣做XD 這題我的作法是,找到對於每一個元素,往左跟往右找到第一個比他小的數字,以兩者為範圍,算high值,最後再取max即。,而往又跟往左找第一個比他小的數字其實一個stack就可以做到了,只要維護stack裡由底往上是遞增就好。 #include <bits/stdc++.h> using namespace std; typedef long long lld; typedef pair<int,int> PII; #define FF first #define SS second const int N = 100000 + 5; lld arr[N], preS[N]; int rr[N], ll[N]; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; while(cin>>n){ for(int i=1;i<=n;i++) cin>>arr[i]; arr[n+1]=0; for(int i=1;i<=n;i++) preS[i] = preS[i-1]+arr[i]; stack<int> ss; for(int i=1;i<=n;i++){ while(!ss.empty() and arr[ss.top()] > arr[i]){ rr[ss.top()]=i-1; ss.pop(); } ss.push(i); } while(!ss.empty()){ ...

[NPSC] 2009初賽 E. 檸檬汽水傳說

題目連結: http://contest.cc.ntu.edu.tw/npsc2009/2009sen.pdf 不 本題複雜度大概要做到$O(n)$或$O(n \log n)$,所以明顯naive的枚舉點對做法是不可能會好的,不妨思考其實每個人都只要往一邊看就好了,而且當看到一個比自己大的數就可以停了,那應該可用stack做,而明顯要用stack做的話,要有一定的單調性,想想後發現遞減的是好的,每次都pop掉比自己小的人並把pop了幾個加上去,不過值得注意的是如果是pop掉跟自己一樣的人的話,記得要把他原本的size記錄下來,之後要擺回去,因為其他人都還是可以跟你做匹配之類的,此外因為每個人都還可以跟隔壁的人溝通,所以當隔壁有人時記得多加一。 #include <bits/stdc++.h> using namespace std; class knight{ private: int __val; long long __size; public: knight(int a,int b){__val=a;__size=b;} int operator++(int a){if(a==0)return __size++;} bool operator<=(int a){return __val<=a;} bool operator==(int a){return __val==a;} friend void operator+=(long long &a, knight b){a+=b.__size;} }; stack<knight> ss; int main(){ int t; scanf("%d",&t); while(t--){ int sz; long long ans=0; scanf("%d...