結果
ひさびさにプロコンに参加した。最近は(Java, Eclipse)ばっかりだったので、(C++, Emacs)でうまく書けるか不安だったが思ったより鬼は黒くなかった。
3/5AC、160位/1925人だった。(配列の最大要素数をミスってて問題DはRuntime Errorで死んでいた。それがなかったら20位くらいだった。もったいない。)
問題D、Eがおもしろかった。解答は以下のとおり。
D. Little Elephant and Array
[l, r]区間に現れる回数がちょうどxとなるような正の整数xの個数を求める問題(1 <= l <= r <= n)。xの候補となるような正の整数はたかだか√n個程度になることに気付くかどうかがポイント。[1, y]区間にそれぞれのxが何回現れるかをメモリ上に保持すると(1 <= y <= n)、1クエリーがO(√n)で捌ける。
using namespace std;
const int MAX_LEN = (int)1e5;
int xs[MAX_LEN];
int nums[500][MAX_LEN+1];
int main() {
int n, m;
cin >> n >> m;
map<int, int> cnt;
for (int i = 0; i < n; i++) {
cin >> xs[i];
cnt[xs[i]]++;
}
vector<int> vs;
for (map<int, int>::iterator itr = cnt.begin(); itr != cnt.end(); itr++) {
if (itr->second >= itr->first)
vs.push_back(itr->first);
}
int l = vs.size();
memset(nums, 0, sizeof(nums));
for (int i = 0; i < l; i++) {
for (int j = 0; j < n; j++) {
nums[i][j+1] = nums[i][j];
if (vs[i] == xs[j])
nums[i][j+1]++;
}
}
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
int ret = 0;
for (int j = 0; j < l; j++) {
if (nums[j][b] - nums[j][a-1] == vs[j])
++ret;
}
cout << ret << endl;
}
return 0;
}
E. Little Elephant and Shifts
長さnの順列a, bが与えられる。bのn個の巡回シフト集合に対して、それとaの距離を求める。2つの順列の距離は、 minimum |i - j|, that ai = bjと定義される。multisetをうまく使うと解ける。multisetまでは分かったんだけど、lower_bound使うってのが閃かなかった。データ構造の使い方を問う良問。
using namespace std;
#define REP(i, n) for(int i=0; i<(int)(n); i++)
#define FOR(i, s, e) for (int i = (int)(s); i < (int)(e); i++)
int main() {
int N;
cin >> N;
vector<int> a(N);
vector<int> b(N);
REP(i, N) cin >> a[i];
REP(i, N) cin >> b[i];
vector<int> x(N);
vector<int> y(N);
// set postions of a[i] and b[i]
REP(i, N) x[--a[i]] = i;
REP(i, N) y[--b[i]] = i;
multiset<int> dis;
REP(i, N) dis.insert(y[i]-x[i]);
REP(k, N) {
int ret = 0x7fffffff;
__typeof(dis.begin()) itr = dis.lower_bound(k);
ret = min(ret, *itr-k);
if (itr != dis.begin())
ret = min(ret, abs((*--itr)-k));
cout << ret << endl;
int head = b[k];
dis.erase(dis.find(y[head]-x[head]));
dis.insert(N-1-x[head]+k+1);
}
return 0;
}
0 件のコメント:
コメントを投稿