あとちょっとだったのに…。
http://yukicoder.me/problems/400
問題
文字列SのT数とは、Sから(元の前後順は入れ替えずに)|T|個の文字を抜き出して連結するとTを作れるような抜き出し位置の組み合わせ数とする。
文字列Sを構成する各アルファベットの登場回数がS[i]とする。
S[i]とTが与えられたとき、考えられるSのT数の最大値を求めよ。
ただし値が2^62以上となるなら"hel"と答えよ。
解法
まず、Tにおける各アルファベットの登場回数がS[i]未満の1個でもあるなら、早々に0を返してしまおう。
アルファベットごとに考える。
例えばT中の'a'に注目したとき、T = ___aaa___aaa___aaaa___のような形をしているとする。
T中連続した対象のアルファベットの数の数列Vを考える(上の例ならV=[3, 3, 4])。
ここでS['a']個のアルファベットを、|V|個に分ける。例えばS['a']=15のとき、W=[5, 4, 6]のように分けられる。
すると、SからTを抜き出すときの'a'の抜き出し方はである。
ここまで来ると、上記積を最大化するようなWを求める問題に帰着できる。
まず初期状態としてW=Vとする。残りS['a']-sum(V)個の'a'をWのどこに振り分けるか考えることになる。
ここは、上記積が大きくなるようなところ振り分けるのがよさそうである。
でW[i]が1個増えてになると、全体の積は倍になる。
よってPriorityQueueで有理数の最大値を取っていくと良い。
ただ、この処理はS['a']-sum(V)回 PriorityQueueを辿るので、S['a']が大きいとTLEする。
はS['a']が大きいととても大きくなるので、2^62を早々に超えそうな場合はさっさと抜けてしまうのが良い。
この積はO(S[i]^sum(V))位の値になるので、S['a']が10^7以上、sum(V)が3以上なら2^62以上になるのは確実だし、sum(V)≦2なら簡単に組み合わせ計算で求められる。
ll S[30]; string T; int L; vector<int> V[26]; int num[26]; struct comp { bool operator()(const pair<pair<ll,ll>,pair<ll,ll>> a, const pair<pair<ll,ll>,pair<ll,ll>> b) const { return a.first.first*b.first.second < a.first.second*b.first.first; } }; __int128 hoge(ll s,vector<int> p) { if(p.empty()) return 1; sort(ALL(p)); __int128 ss=s; if(s>10000000) { if(p.size()==1) { if(p[0]==1) return s; if(p[0]==2) return ss*(ss-1)/2; } if(p.size()==2 && p[0]==1 && p[1]==1) return (ss/2)*(ss-ss/2); return 1LL<<62; } __int128 pat=1; priority_queue<pair<pair<ll,ll>,pair<ll,ll>>,vector<pair<pair<ll,ll>,pair<ll,ll>>>,comp> Q; FORR(r,p) s-=r, Q.push(make_pair(make_pair(r+1,1),make_pair(r,r))); while(s--) { auto r=Q.top(); Q.pop(); pat=pat * r.first.first / r.first.second; if(pat>=1LL<<62) return pat; r.second.first++; Q.push(make_pair(make_pair(r.second.first+1,r.second.first+1-r.second.second),r.second)); } return pat; } void solve() { int i,j,k,l,r,x,y; string s; FOR(i,26) cin>>S[i]; cin>>T; L=T.size(); V[T[0]-'a'].push_back(1); FOR(i,L-1) { if(T[i]==T[i+1]) V[T[i+1]-'a'].back()++; else V[T[i+1]-'a'].push_back(1); } FOR(i,L) num[T[i]-'a']++; __int128 ret=1; FOR(i,26) if(num[i]>S[i]) return _P("0\n"); FOR(i,26) { __int128 pat=hoge(S[i],V[i]); if(pat>=1LL<<62) return _P("hel\n"); ret*=pat; if(ret>=1LL<<62) return _P("hel\n"); } cout<<(ll)ret<<endl; }
まとめ
解法の1行目に書いたことをやってなくてWAしてた。