羅区タイトルページポータル&codeforcesタイトルページポータル
与えられた\(\ N-)テキストの単語、最初に\を(私は\)長さワードがある\(len_i \) 、ピア・ワードに置かいくつかの行のテキスト段落の傍受を(単語が丸みを帯びている)が必要です行の長さを超えないように、スペースで区切られた\(m個\)を、我慢\(S \)行。方法を切断ワードの最大数を傍受しようとしています。
\(N \で[1,10 ^ 6]、\和\ limits_ {i = 1} ^ nlen_i \における[1,5 \ times10 ^ 6]、MS \で[1,10 ^ 6] \)。
この質問は、ACはまだ非常に簡単です望んでいます。最初の考慮事項の列挙は、撮影した\(1 \) 、その後の言葉、バックアップどのように多くの単語拡張アプリケーションを計算し、最終的に取るように\(\最大\を)。これは無邪気に貪欲することができ、どのように多くの単語拡張アプリケーションを計算するためにバックアップを作成する方法に焦点を当てています。最初の検索\(SPL \)配列を、それが最初表す\(iは\)単語が後最初までに適用するように拡張し始める\(spl_i-1 \)単一ラインワードです。明らかに、「拡張アプリケーションかどうかを第二の\(X \)単調、単一ライン上の単語」\(SPL \)アレイをすることができる(\ \ mathrm O(N \の log_2n)\) 接頭辞と半分とシークアウト。そうから\(iは\)バック拡張アプリケーションまでの単語の単語の数である(\ \ underbrace {spl_ {spl_ {SPL _ {\ cdots_ {I}}}}} _ {Sの\テキスト{ 時刻} SPL \マッピングテキスト{}} -私は\) 。これは明らかに、合計であるている\(\ mathrm O(N \ log_2n)\) 乗算決定。したがって、\(\ mathrm O(n個の\ log_2n)\) 複雑さは非常に簡単です。
そして、私彼ら完璧なOIerの追求は、この複雑さが実現できている(\ mathrm O(n)の\を \) それは?\(\ログ\)複雑さを有している場合、\(2 \) 2 -シーク\(SPL \)のアレイと、\(S \)の時間は、\(SPL \)マップは、私たちは一つ一つを見て。
最初は求めている\(SPL \)の配列を。見つけることは困難で、\(SPL \)本質的に単調なアレイ、すなわち\(spl_i \ルspl_。1} + {Iが\) 、我々は、再帰的なシークから前方に移動することができる\(spl_iの\)単に、\(spl_は{I + 1} \) する(Iは\)\アプリケーションを拡張することができるかどうかをテストするために背面から。境界であることを特徴と\(spl_ 1} = {+ N-N - +。1 \) 。このように全ての単語が等しく施す共有\(\ mathrm O(N) \) 倍、時間複雑度がない\(\ログ\) 。
次のマップです。まだ使用して\(SPL \)全ての場合、アレイの単調性を\(Iは\)と\(spl_iの\)エッジとの間にあっても、\は、(i = spl_i \)の縁部に接続されていない、それが形成します森は、つつ\(iは\)する\(S \)の時間は明らかにマッピングノードに相当する\(iは\)\(\分(S、dep_i )\) 世代の祖先。我々は、再帰スタック維持しながらDFSは、各木の森\(STKを\) 、そう\(\ mathrm O(1) \) を見つけることができ、ノード\が(私は\)\(\分(複数可、dep_i)\)世代の祖先は、複雑さも一体となって\(\ mathrm O(N) \) 。
以下のコードを貼り付けます。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1000000;
int n/*单词数*/,m/*每行最多能放的长度*/,s/*最多能放的行数*/;
string a[N+1];//单词们
int Sum[N+1];//前缀长度和(每个单词后面加上空格)
vector<int> son[N+2];int fa[N+2];//树,fa即spl数组
int stk[N+1],top;//递归栈
int ans[N+2];//从第i个单词开始最多能延伸的单词数
void dfs(int x){//对树DFS
stk[top++]=x;//将此节点入栈
ans[x]=stk[max(0,top-1-s)]-x;//O(1)找min(s,dep[i])辈祖先
for(int i=0;i<son[x].size();i++){
int y=son[x][i];
dfs(y);
}
top--;//出栈
}
int main(){
cin>>n>>s>>m;
for(int i=1;i<=n;i++)cin>>a[i],Sum[i]=Sum[i-1]+a[i].size()+1/*预处理前缀和*/;
fa[n+1]=n+1;//递推边界
for(int i=n;i;i--){//从后往前递推
fa[i]=fa[i+1];
while(Sum[fa[i]-1]-Sum[i-1]-1>m)fa[i]--;//从后往前试
if(fa[i]!=i)son[fa[i]].pb(i);//连边
}
// for(int i=1;i<=n+1;i++)cout<<fa[i]<<" ";puts("");
for(int i=1;i<=n+1;i++)if(fa[i]==i)top=0,dfs(i);//DFS每棵树
int mx=*max_element(ans+1,ans+n+2);//最大答案
for(int i=1;i<=n+1;i++)if(ans[i]==mx){
while(s--){//输出
for(int j=i;j<fa[i];j++)cout<<a[j]<<(j<fa[i]-1?" ":"\n");
i=fa[i];
}
return 0;
}
}