UVa 11235 Problem F: Frequent values
問題概要
http://uva.onlinejudge.org/external/112/11235.html
非増加列が与えられる。
範囲を指定するクエリが与えられる。
その範囲のなかの最頻値の頻度を答えよ。
解法
セグメントツリー+分割統治
ノードは
- 自分の範囲のなかでの最頻値の頻度
- 一番右の値が連続する数
- 一番右の値
- 一番左の値が連続する数
- 一番左の値
を持てばよい。
二つの領域をマージするとき、
- どちらか一方の最頻値
- 両方の領域にまたがり、連続する数字
が、最頻値になり得るので、上記の情報を持つ必要がある。
初めてのセグメントツリー。
あまり、スマートに書けなかった気がする。
ソース
簡潔化前 下に簡潔化後
#include<iostream> #include<string> #include<algorithm> #include<vector> #include<map> #include<set> #include<cstdlib> #include<cstdio> #include<cmath> #include<sstream> #include<cassert> #include<queue> #include<stack> #include<bitset> #include<cstring> #define REP(i,b,n) for(int i=b;i<(int)n;i++) #define rep(i,n) REP(i,0,n) #define ALL(C) (C).begin(),(C).end() #define FOR(it,o) for(__typeof((o).begin()) it=(o).begin(); it!=(o).end(); ++it) using namespace std; const double EPS = 0.00000001; const int INF = 100000000; typedef long long lli; const int N = (1 << 17); int t[N]; int n; class Data{ public: int rCnt, rNum, lCnt, lNum, maxCnt; Data(){ } Data init(){ rCnt = rNum = lCnt = lNum = maxCnt = 1; return *this; } Data zero(){ rCnt = rNum = lCnt = lNum = maxCnt = -INF; return *this; } bool operator<(const Data &d)const{ if(lNum != d.lNum) return lNum < d.lNum; return rNum < d.rNum; } }; Data unite(Data d1, Data d2){ Data d; d.lNum = d1.lNum; d.rNum = d2.rNum; d.rCnt = d2.rCnt; d.lCnt = d1.lCnt; d.maxCnt = max(d1.maxCnt, d2.maxCnt); if(d1.rNum == d2.lNum){ d.maxCnt = max(d.maxCnt, d1.rCnt + d2.lCnt); if(d1.lNum == d1.rNum)d.lCnt = max(d.lCnt, d1.rCnt + d2.lCnt); if(d2.rNum == d2.lNum)d.rCnt = max(d.rCnt, d1.rCnt + d2.lCnt); } return d; } vector<Data> RES; class Tree{ public: Data d[2*N]; int R; Tree(){ } Tree(int n){ int e = 1; while(n >= e){ e *= 2; } R = e; assert(e < 2*N); set(0, e-1, 1); } void set(int b, int e, int id){ if(b >= n){ d[id] = Data().zero(); return; } if(b == e){ d[id] = Data().init(); d[id].rNum = d[id].lNum = t[b]; return; } int rid, lid, rb, re, lb, le; rid = id * 2 + 1; lid = id * 2; lb = b; le = b + (e - b+1)/ 2 - 1; rb = b + (e - b+1)/2; assert((e - b+1)%2 == 0); re = e; set(lb, le, lid); set(rb, re, rid); d[id] = unite(d[lid], d[rid]); } int query_init(int b, int e){ RES.clear(); query(b, e, 0, R-1, 1); sort(ALL(RES)); rep(i, RES.size() -1){ RES[i+1] = unite(RES[i], RES[i+1]); } return RES.back().maxCnt; } void query(int b, int e, int tb, int te, int id){ if( b <= tb && e >= te){ RES.push_back(d[id]); return; } int nid = id * 2 + 1; int ntb = tb + (te-tb+1)/2; int nte = te; if( b <= nte && ntb <= e){ query(b, e, ntb, nte, nid); } nid = id * 2; ntb = tb; nte = tb + (te-tb + 1)/2 - 1; if( ntb <= e && nte >= b){ query(b, e, ntb, nte, nid); } } }; int main(){ while(cin >> n){ if(n == 0)break; int q; cin >> q; rep(i, n)cin>> t[i]; Tree t(n); rep(i, q){ int b, e; cin >> b >> e; RES.clear(); cout << t.query_init(b-1, e-1) << endl; } } return 0; }
あまりに、あれなので、少し簡潔にしてみる。
#include<iostream> #include<string> #include<algorithm> #include<vector> #include<map> #include<set> #include<cstdlib> #include<cstdio> #include<cmath> #include<sstream> #include<cassert> #include<queue> #include<stack> #include<bitset> #include<cstring> #define REP(i,b,n) for(int i=b;i<(int)n;i++) #define rep(i,n) REP(i,0,n) #define ALL(C) (C).begin(),(C).end() #define FOR(it,o) for(__typeof((o).begin()) it=(o).begin(); it!=(o).end(); ++it) using namespace std; const double EPS = 0.00000001; const int INF = 100000000; typedef long long lli; const int N = (1 << 17); int t[N]; int n; class Data{ public: int rCnt, rNum, lCnt, lNum, maxCnt; }; Data unite(Data d1, Data d2){ Data d; d.lNum = d1.lNum; d.rNum = d2.rNum; d.rCnt = d2.rCnt; d.lCnt = d1.lCnt; d.maxCnt = max(d1.maxCnt, d2.maxCnt); if(d1.rNum == d2.lNum){ d.maxCnt = max(d.maxCnt, d1.rCnt + d2.lCnt); if(d1.lNum == d1.rNum)d.lCnt = max(d.lCnt, d1.rCnt + d2.lCnt); if(d2.rNum == d2.lNum)d.rCnt = max(d.rCnt, d1.rCnt + d2.lCnt); } return d; } class Tree{ public: Data d[2*N]; int V; Tree(int n){ int e = 1; while(n >= e){ e *= 2; } V = e; set(0, e, 1); } Data set(int b, int e, int id){ if(b >= n){ d[id] = (Data){0, 0, 0, 0, 0}; return d[id]; } if(b == e-1){ d[id] = (Data){1, t[b], 1, t[b], 1}; return d[id]; } Data L = set(b, (e+b)/2, 2*id ); Data R =set((e+b)/2, e, 2*id + 1); d[id] = unite(d[2*id], d[2*id+1]); return d[id]; } int query_init(int b, int e){ return query(b, e, 0, V, 1).maxCnt; } Data query(int b, int e, int tb, int te, int id){ if(te <= b || e <= tb)return (Data){0, 0, 0, 0, 0}; if( b <= tb && e >= te){ return d[id]; } Data L = query(b, e, tb, (tb+te)/2, id*2); Data R = query(b, e, (tb+te)/2, te, id*2+1); return unite(L, R); } }; int main(){ while(cin >> n){ if(n == 0)break; int q; cin >> q; rep(i, n)cin>> t[i]; Tree t(n); rep(i, q){ int b, e; cin >> b >> e; cout << t.query_init(b-1, e) << endl; } } return 0; }
このくらいに最初から書きたいものです。
プログラミングで、範囲を指定する時、下限はinclusiveで上限はそうではないのは
実装上、簡単だから、ということに気づいた。
ほかのものを実装するときもそうしたいと思う。