TopCoder SRM 603 Div1 Hard SumOfArrays
問題
長さn項の二つの数列a[i], b[i]が与えられる。
bの順序を好きに入れ替えて数列b'[i]を作る。
c[i] = a[i] + b'[i]という数列に、なるべく同じ数が出るようにbを並べ替える。
このとき、同じ数の出現回数の最大値と、その数を求めよ。
出現回数の最大値になる数が複数ある場合は、もっとも大きい数を求めよ。
制約条件
n≦10万
a[i]≦10万
制限時間4秒
方針
editorial見た。
c[i]に数xが出現する回数の最大値をC[x]とする。
C[x]なる数列を効率的に求めたい。
以下A[i]をa[j] = iなるjの個数、B[i]をb[j] = iなるjの個数とする。
xを固定すると、
C[x] = Σ[i + j = x]min(A[i], B[j])
で、これは謎の発想により見方を変えるとi + j = x, A[i]≧k, B[j]≧kかつk>0である
ペア(i, j, k)の個数に等しい。
このペアを、kの値により場合わけして考える。
kが10以上のとき、A[i]≧kとなるAの要素は1万程度しかないのでループして数えられる。
kが9以下のときは、k=1から9までループをまわす。
C[i + j] += (A[i] >= k) * (B[j] >= k)であるので、
これはすなわち畳み込みで、高速フーリエ変換を使えば高速に計算できる。
制限時間が4秒なのでギリギリ間に合う。
ソースコード
Complex A[SZ], B[SZ]; void calc(int k, const vi &a, const vi &b){ rep(i, MX){ A[i] = a[i] >= k ? 1 : 0; B[i] = b[i] >= k ? 1 : 0; } for(int i = MX; i < SZ; i++) A[i] = B[i] = 0; fft(SZ, 2*PI/SZ, A); fft(SZ, 2*PI/SZ, B); rep(i, SZ) A[i] *= B[i]; fft(SZ, -2*PI/SZ, A); } class SumOfArrays { public: string findbestpair(int n, vector <int> Aseed, vector <int> Bseed) { vi a = gen(Aseed, n), b = gen(Bseed, n); vector<ll> ans(2*MX); //big k vector<pi> biga, bigb; rep(i, a.size()){ if(a[i] >= 10) biga.pb(mp(a[i], i)); if(b[i] >= 10) bigb.pb(mp(b[i], i)); } rep(i, biga.size()) rep(j, bigb.size()){ ans[biga[i].second + bigb[j].second] += min(biga[i].first, bigb[j].first) - 9; } //small k for(int k = 1; k < 10; k++){ calc(k, a, b); rep(i, 2*MX) ans[i] += (int)(A[i].real() / SZ + 0.5); } pair<ll, int> p(-1, -1); rep(i, ans.size()) p = max(p, mp(ans[i], i)); stringstream ss; ss << p.first << " " << p.second; return ss.str(); } vi gen(const vi &s, int n){ vi a, res(MX); a.pb(s[0]); a.pb(s[1]); rep(i, n - 2){ a.pb(((ll)a[i + 1] * s[2] + (ll)a[i] * s[3] + s[4]) % s[5]); } rep(i, n) res[a[i]]++; return res; } };