[CSP2019]分割問題解決レポート

【CSP2019】分割

問題の意味

そこ\(N- \)非負整数\(a_iを\) \((N- \ 4 * 10 ^ル7)\) いくつかの部分に分割する、と呼ばれる(S_I \)\、必要(S_ {\ 1} + I \ GE S_I \)
提供\(RES = \ sum_ {I} = ^ {K}。1 S_I ^ 2 \)
需要\(RES \)最小

思考

64のPTS

実際の筆記試験。

セット\(F [I] [J ] \) 最初のセグメントの開始点である\(I \) 終点\(J \)場合\(RES \)最低。
単純なアプローチは、直接列挙\(I 、J、K \) \(F [I] [J ] \) から\([J] F [K ] \) 上に移します。

最適化:
中間転写ポイント考えてみましょう\(J \)を、各\(I <J \) 法的\(K \)の範囲が増加している、それだけで列挙することが可能である\(I、Jを\)その後、ポインタ\(K \)\(sum_j-sum_ {I- 1} \) 連続的に前進忘れ、及び最小値を更新することができます。

88のPTS

設けられている\(K \)ブロック、二つの特性を得ることができる。
自然の1\(K \) より好ましい実施形態は、
知覚的に認識:\(A + B ^ 2 ^ 2 \ル(A + B)^ 2 \)
さらに推測
特性2\(S_K \) 小さい好ましい実施形態
知覚認識:\(S_Kが\)に正面を強制的に、正当なプログラムを作るために、小さい\(S \ )ので、可能な限り小さくなければならない\(A \)非負整数、(S \)\値が小さいほど、\(K \)以上、より好ましくは1プログラムの性質に応じ。

だから我々は最も小さい、それがエンドポイントであるときを見つけるために、それぞれのための単調なキューを維持することができる\(S_K \)
新しい要素は単調な詳細の一部としてだけでなくキューに追加されている場合は、コードを参照してください。

100pts

高精度を追加するための上記のアプローチ (高精度なものは長い...大丈夫、忘れてしまいました)

コード

64pts


#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e3+7;
const ll inf=5e18;
int n,ty;
ll a[N],sum[N],f[N][N],ans=inf;
void read(){ }
ll p2(ll x){ return x*x; }
int main(){
  //freopen("divd.in","r",stdin);
  cin>>n>>ty;
  if(ty) read();
  else for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; }
  memset(f,127,sizeof(f));
  for(int i=1;i<=n;i++) f[i][n]=p2(sum[n]-sum[i-1]);
  for(int j=n;j>=1;j--){
    int k=n;
    ll minx=inf;
    for(int i=1;i<=j;i++){
      while(p2(sum[j]-sum[i-1])<=p2(sum[k]-sum[j])){
    minx=min(minx,f[j+1][k]);
    k--;
      }
      f[i][j]=min(f[i][j],minx+p2(sum[j]-sum[i-1]));
    }
  }
  for(int i=1;i<=n;i++) ans=min(ans,f[1][i]);
  printf("%lld\n",ans);
  return 0;
}

88pts


#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e7+7;
int n,ty,pre[N],que[N],t1,t2;
ll a[N],sum[N],lst[N],ans;
void read(){};
ll p2(ll x){ return x*x; }
int main(){
  //  freopen("divd.in","r",stdin);
  //  freopen("x.out","w",stdout);
  cin>>n>>ty;
  if(ty) read();
  else for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; }
  t1=t2=1;
  que[1]=0;
  for(int i=1;i<=n;i++){
    //        printf("%d: ",i); for(int j=t1;j<=t2;j++) printf("%d ",que[j]); putchar('\n');
    while(t1<t2&&lst[que[t1+1]]<=sum[i]-sum[que[t1+1]]) t1++;
    pre[i]=que[t1];
    lst[i]=sum[i]-sum[pre[i]];
    while(t2>=t1&&lst[que[t2]]>=lst[i]+sum[i]-sum[que[t2]]) t2--;
    que[++t2]=i;
  }
  int p=n;
  while(p){
    ans+=lst[p]*lst[p];
    p=pre[p];
  }
  //     for(int i=1;i<=n;i++) printf("pre[%d]: %d\n",i,pre[i]);
  printf("%lld\n",ans);
  return 0;
}

おすすめ

転載: www.cnblogs.com/brucew-07/p/11896792.html