[POJ 2452]RMQ、二分枚举

【题目大意】给定一个长度为N的没有相同元素的序列,然后让你求一个连续子串A[I],A[I+1]….A[J]使得A[I]是这短子串中的最小值,A[J]是这段子串中的最大值,并使得它J-I的值最大。

【算法分析】我先假设确定了起点,然后考虑在O(lgN)的复杂度里找到对应的最长长度。对于这个问题,我们可以考虑先找出以I为起点的区间范围,即以A[I]为最小值的区间范围。找出这个范围以后,只需要再找出其中的最大值所在的位置,就已经找出了以I为起点对应的最长区间了。(因为这个数在可取值的范围里最大,所以不能再延伸区间了)。对于实现这个算法,我们很容易得到确定了起点的最小值单调递减,于是我们可以二分得到这个范围,复杂度O(lgN)。然后那个取最大值和最小值均可以用RMQ在O(1)的复杂度内搞出来。由于预处理RMQ复杂度也为O(NlgN),所以整个算法的复杂度是O(NlgN)。

【其它】1A,今天的第10题,刷完睡觉

6430690 edward2 2452 Accepted 12516K 766MS G++ 1971B 2010-02-10 01:08:32

【CODE】

#include

[SPOJ 744]连续最长1..M排列

【题目大意】给定一个1..N的序列,然后让你求一段连续的子串包含1..M中所有的数字,且不重复。最后输出max(M)

【算法分析】

定义S[I]=前I个数的和。

NEXT[I]为下一个和当前这个数相同的数的位置

LAST[I]为前一个和当前这个数相同的数的位置

如果输满足条件的一个连续串的话,必然是没有重复的数字的,并且和为(M+1)*M/2。(等差数列求和)

已知结果串必然包含1,所以我们以1为标志,进行左右扫描。

情况1

假设这个1所能达到的最大M在左边,i为这个1的位置,则j向左扫描

q=max(a[j],q)

p=min(next[j],q)

显然p不能<=i,另外j+q-1

情况2和情况1对称,不再赘述。

【其它】1A

3251945 2010-02-09 16:03:34 cwj Longest Permutation accepted 0.96 4.7M

C++

4.3.2

【CODE】

#include #include #define min(x,y) (x)<(y)?(x):(y)
#define max(x,y) (x)>(y)?(x):(y)
const int N=111111;
int n,a[N],last[N],next[N],lpos[N],s[N];

void init(){
    s[0]=0;
    for (int i=1;i<=n;i++){
        s[i]=s[i-1]+a[i];
        next[lpos[a[i]]]=i;
        last[i]=lpos[a[i]];
        lpos[a[i]]=i;
    }   
    for (int i=1;i<=n;i++)
      if (lpos[i]!=0) next[lpos[i]]=n+1;
}   

void work(){
    int ans=0;
    for (int i=1;i<=n;i++)
      if (a[i]==1){
          ans=max(1,ans);
          int q=1,p=next[i];
          for (int j=i;j>last[i];j–){
              q=max(a[j],q);
              p=min(next[j],p);
              if (p<=i) break;
              if (j+q-1<=n && j+q-1

                ans=max(ans,q);
          }   
          q=1,p=last[i];
          for (int j=i;j              q=max(a[j],q);
              p=max(last[j],p);
              if (p>=i) break;
              if (j-q+1>=1 && j-q+1>p && s[j]-s[j-q]==q*(q+1)/2)
                ans=max(ans,q);
          }   
      }
    printf("%dn",ans);   
}   

int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    init();
    work();
}   

[SGU 177]利用并差集处理矩形覆盖问题**

【题目大意】给定一个N*N的矩阵和M次染色,问最后是白色的格子还有多少个?

【算法分析】这题一看是线段树or矩形切割,但是仔细一想,可以发现一种新的算法,这中间利用了并差集。定义next[i,j]表示如果要染i,j这一个格子的话,应当去染哪一个格子。考虑我们写矩形切割的“浮冰法”,从后面开始进行insert,这样的话,后insert的必须避开先insert得。所以可以利用这个next进行跳跃。我这个next只针对当前行,就是next[i,j]的跳跃位置是对于第i行而言的。

初始时next[i,j]=j,表示要染这一格,那么就应该染这一格,比较拗口

然后如果对这一格被染色的话,那么next[i,j]=j+1,这样下一次就会越过我跳到下一格了,然后用并差集进行路径压缩,这样就能在平摊O(1)的复杂度调到下一个能染得格了!

这个算法中,对每个格子最多碰一次,所以染色的复杂度为O(N^2),但是由于对于每一次染色,都必须枚举行,所以总复杂度是O(N^2+NM)。

【其它】1A,时限7S

997757 09.02.10 16:53 edward 177 .CPP Accepted 796 ms 9739 kb

【CODE】

#include #include const int N=1111;
const int M=5555;
int n,m,next[N][N],x1[M],x2[M],y1[M],y2[M],color[N][N],dc[M];

void swap(int &x,int &y){
    int tmp=x; x=y; y=tmp;
}   

void init(){
    for (int i=0;i<=n+1;i++)
      for (int j=0;j<=n+1;j++)
        next[i][j]=j;
}   

int findnext(int i,int j){
    if (next[i][j]==j) return j;
    next[i][j]=findnext(i,next[i][j]);
    return next[i][j];
}   

void work(){
    memset(color,200,sizeof(200));
    for (int tmp=m;tmp>=0;tmp–)
      for (int i=x1[tmp];i<=x2[tmp];i++){
          for (int j=y1[tmp];j<=y2[tmp];)
            if (next[i][j]==j){
                color[i][j]=dc[tmp];
                next[i][j]=j+1;
                j++;
            }
            else j=findnext(i,j);
      }
}   

void print(){
    int ans=0;
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
        if (color[i][j]==0) ans++;
    printf("%dn",ans);
}   

int main(){
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    scanf("%d%d",&n,&m);
    x1[0]=1; y1[0]=1; x2[0]=n; y2[0]=n;
    for (int i=1;i<=m;i++){
        int lx,ly,rx,ry;
        char co;
        scanf("%d%d%d%d %c",&lx,&ly,&rx,&ry,&co);
        if (lx>rx) swap(lx,rx);
        if (ly>ry) swap(ly,ry);
        x1[i]=lx; x2[i]=rx; y1[i]=ly; y2[i]=ry;
        if (co==’b’) dc[i]=1;
    }   
    init();
    work();
    print();
    return 0;
}   

[POJ 2029]矩阵处理

【题目大意】给定N*M的矩形和T个点,让你求A*B的子矩形最多能覆盖几个点。

【算法分析】压缩列,再扫描行。这个方法是O(N^3)的,最快的算法请参见这道题的加强版:http://hi.baidu.com/edwardmj/blog/item/b2ffc2f83570dd9d59ee900a.html

【其它】1A

6429770 edward2 2029 Accepted 508K 16MS G++ 924B 2010-02-09 20:40:21

【CODE】

#include

[POJ 1019]数字处理

【题目大意】有这样一个序列,1,112,112123,1121231234,112123123412345…,求它的第X位是多少。

【算法分析】

构造,分步骤来求,逐步逼近答案。

先对序列进行分组,即按1,12,123,1234,12345这样分组,利用递推求出前I组数的位数,即Sum[I]:=Sum[I-1]+F[I],F[I]:=F[I-1]+Trunc(ln(I)/ln(10))+1。

这样,我们可以很容易确定第X位的所在组(当组大于40000时,位数已经超过Maxlongint)

然后在用While循环求出第X位是这个组中的第几个数,再求出是这个数的第几位即可。

【其它】复杂度约为O(sqrt(n))

6429639 edward2 1019 Accepted 884K 16MS G++ 1094B 2010-02-09 20:10:37

【CODE】

#include