【HDU6602】最長サブアレイ

タイトル効果:サブストリング法的ストリングを満たす必要があり、長い正規のサブストリング最長を求め配列指定された文字列、[1、C]は数よりも大きいかまたはK 0に等しいです。

ソリューション:

これは、必要最小限の範囲右端点の点を含む右端点として定義されます

その後、初期化がO(n)の各点で計算することができる右端点

点間隔に右端点を含む最小間隔を定義します

異なる定義間隔ときに、2つの最小間隔含む区間の左端の要素ではない、同時に異なる2

合法的な部分文字列を考えることは困難ではない場合にのみ、すべての異なる間隔の左側のほとんどは、この時点で正当なサブストリング、ストリングに含まれている場合

このため、分割点として違法範囲、この違法左点の範囲を考えることは困難が存在しない場合には、左右に再帰的な操作を分割

除算とそうとアプローチを征服する、サブストリング尋ねる開始[1、n]は正当なもので、法的な場合には、分割ポイントとして分割し、征服するために、すべての違法左のポイントを見つけ、法的に答えではないにも続けます

ボトルネックの複雑さである、我々はそれを最適化することを検討違法左のポイントを探します

右の点のポイント場所を、セグメントツリーの維持、最大セグメントツリーメンテナンス右点、及び提供

次に、時間セグメントツリーを考えるが困難Rよりも大きいの右端点かどうかを確認する権利を得るために、[L、R]を依頼する必要があり、正規より大きくない、別のセグメントツリーによって返される値は、分割点を分割法的ポイントではありません治療

私は最初のツリーラインに加わった際に、厳格なパーティションは、ポイントを削除するには、ポイントを削除すると、アカウントに私は最高に残っすべて同じ色を取って、これだけ左から右へ、各色の左端の間隔を置きます追加ツリーラインの範囲のポイントを削除する色の下で、私たちは何も見つからないか、間違った答えを保証することはできませんしながら、

各点を加え、再度削除ので、O(nlogn)の時間計算されるので、この問題ができ

コード:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#define INF 1000000007
using namespace std;
int n,c,k,tans;
int a[100001],nxt[100001],hd[100001];
int minsb[100001];
struct node
{
  int p,minr;
}dat[100001*20];

void pushup(int pos)
{
    if(dat[pos<<1].minr>=dat[pos<<1|1].minr)dat[pos]=dat[pos<<1];
    else dat[pos]=dat[pos<<1|1];
}
void change(int l,int r,int p,int v,int pos)
{
    //printf("%d %d %d\n",l,r,p);
    if(l==r && l==p)
    {
      dat[pos]=(node){p,v};
      return;
    }
    int mid=l+r>>1;
    if(p<=mid)change(l,mid,p,v,pos<<1);
    else change(mid+1,r,p,v,pos<<1|1);
    pushup(pos);
}
node ask(int l,int r,int al,int ar,int pos)
{
    if(l==al && r==ar)return dat[pos];
    int mid=l+r>>1;
    node t1,t2;
    if(ar<=mid)return ask(l,mid,al,ar,pos<<1);
    if(al>mid)return ask(mid+1,r,al,ar,pos<<1|1);
    t1=ask(l,mid,al,mid,pos<<1);
    t2=ask(mid+1,r,mid+1,ar,pos<<1|1);
    if(t1.minr>=t2.minr)return t1;
    else return t2;
}
void work(int l,int r)
{
    //printf("%d %d\n",l,r);
    if(l>r)return;
    node td=ask(1,n,l,r,1);
    //printf("  %d %d\n",td.p,td.minr);
    if(l==r)
    {
      if(td.minr<=r)tans=max(tans,1);
      change(1,n,l,0,1);
      if(nxt[l])change(1,n,nxt[l],minsb[nxt[l]],1);
      return;
    }
    if(td.minr>r)
    {
      work(l,td.p-1);
      change(1,n,td.p,0,1);
      if(nxt[td.p])change(1,n,nxt[td.p],minsb[nxt[td.p]],1);
      work(td.p+1,r);
    }
    else 
    {
      tans=max(tans,r-l+1);
      for(int i=l;i<=r;i++)
      {
        //printf("*%d*  %d\n",i,nxt[i]);
        change(1,n,i,0,1);
        //printf("==-=-=-=-=-=-=\n");
        if(nxt[i])change(1,n,nxt[i],minsb[nxt[i]],1);
      }
      return;
    }
}
void init()
{
    memset(a,0,sizeof(a));
    memset(nxt,0,sizeof(nxt));
    memset(hd,0,sizeof(hd));
    memset(minsb,0,sizeof(minsb));
    for(int i=1;i<=n;i++)change(1,n,i,0,1);
    tans=0;
}
int main()
{
    while(scanf("%d%d%d",&n,&c,&k)!=EOF)
    {
      init();
      for(int i=1;i<=n;i++)scanf("%d",&a[i]);
      for(int i=n;i>0;i--)
      {
        nxt[i]=hd[a[i]];
        hd[a[i]]=i;
      }
      for(int i=1;i<=c;i++)
      {
        int t=hd[i],l=1,j;
        if(!t)continue;
        j=t;
        while(l<k && j)
        {
          j=nxt[j];
          if(j)l++;
        }
        if((!j) || l<k)
        {
          while(t){minsb[t]=INF;t=nxt[t];}
        }
        else
        {
          while(t && j){minsb[t]=j;j=nxt[j];t=nxt[t];}
          while(t){minsb[t]=INF;t=nxt[t];}
        }
      }
      for(int i=1;i<=c;i++)if(hd[i])change(1,n,hd[i],minsb[hd[i]],1);
      work(1,n);
      printf("%d\n",tans);
      //for(int i=1;i<=n;i++)printf("%d ",minsb[i]);
    }
    return 0;
}

心得:考场上很快就想到了区间的做法,但是最后维护线段树时只放最左边的想法一直没有突破导致浪费了一点时间,还需要更加努力啊

おすすめ

転載: www.cnblogs.com/worcher/p/11271000.html