块状链表实在难写,反正没一次写对的,于是我用Treap来Cheat呵呵,其实Treap的话呢,插入删除和打印操作都是O(nlogL)(n:操作数的长度,L:总长度)的,比起块状链表华丽丽的O(sqrt(n))来啊那可是大大的不美,所以在时间上的差距也很明显(6倍啊OTZ),不过还是AC了,嗯。话说Treap比起朴素的数组实现来说,最大的优势在于LogL上。如果把LogL看成是常数,那么这个插入删除的总复杂度几乎可以看成是O(L)的,其实还是很好的,因为单单把数据读进去就要O(L)的复杂度。朴素的实现,插入或删除是O(n+L)的,积累起来最坏O(L^2),而Treap来说最坏情况期望是O(LlogL),虽然很Cheat,不过还是很有内涵的Cheat。

最近写Treap写得越来越顺手,估计真正比赛的时候如果遇上Treap的题应该也能写对。

从思想上来说呢,把序列存在Treap里头确实不太自然。其实很简单,只要把序列想象成一个个离散的元素加上一个位置标号,然后按照位置标号排序得到的有序队列就可以了。然后按照那个位置标号建立平衡树,就能在平衡树上实现串的操作了。

于是把程序和时间比较贴出来,asdf是HQM的标程。

开始测试 asdf
   正在编译 asdf.editor ...成功
      正在测试 asdf.editor.0(editor0.in)... 正确 0.308秒 得分: 10
      正在测试 asdf.editor.1(editor1.in)... 正确 0.003秒 得分: 10
      正在测试 asdf.editor.2(editor2.in)... 正确 0.072秒 得分: 10
      正在测试 asdf.editor.3(editor3.in)... 正确 0.085秒 得分: 10
      正在测试 asdf.editor.4(editor4.in)... 正确 0.085秒 得分: 10
      正在测试 asdf.editor.5(editor5.in)... 正确 0.047秒 得分: 10
      正在测试 asdf.editor.6(editor6.in)... 正确 0.053秒 得分: 10
      正在测试 asdf.editor.7(editor7.in)... 正确 0.050秒 得分: 10
      正在测试 asdf.editor.8(editor8.in)... 正确 0.138秒 得分: 10
      正在测试 asdf.editor.9(editor9.in)... 正确 0.258秒 得分: 10
      asdf.editor 的总分: 100
   asdf 的总分: 100
开始测试 ti
   正在编译 ti.editor ...成功
      正在测试 ti.editor.0(editor0.in)... 正确 1.771秒 得分: 10
      正在测试 ti.editor.1(editor1.in)... 正确 0.003秒 得分: 10
      正在测试 ti.editor.2(editor2.in)... 正确 0.663秒 得分: 10
      正在测试 ti.editor.3(editor3.in)... 正确 0.743秒 得分: 10
      正在测试 ti.editor.4(editor4.in)... 正确 0.734秒 得分: 10
      正在测试 ti.editor.5(editor5.in)... 正确 0.524秒 得分: 10
      正在测试 ti.editor.6(editor6.in)... 正确 0.526秒 得分: 10
      正在测试 ti.editor.7(editor7.in)... 正确 0.518秒 得分: 10
      正在测试 ti.editor.8(editor8.in)... 正确 0.668秒 得分: 10
      正在测试 ti.editor.9(editor9.in)... 正确 0.920秒 得分: 10
      ti.editor 的总分: 100
   ti 的总分: 100
 

 

#include <cstdio>
#include <cstdlib>
#include <cstring>

FILE *in =fopen("editor.in", "r");
FILE *out =fopen("editor.out", "w");

typedef struct node
{
  int k, size;
  char ch;
  node *l, *r;
} node;

node *t, *nul;
int cursor=1;

node *Right_Rotate(node *t)
{
  t->l->size=t->size;
  t->size=t->l->r->size+1+t->r->size;
  node *t2=t->l;
  t->l=t2->r;
  t2->r=t;
  t=t2;
  return t;
}

node *Left_Rotate(node *t)
{
  t->r->size=t->size;
  t->size=t->r->l->size+1+t->l->size;
  node *t2=t->r;
  t->r=t2->l;
  t2->l=t;
  t=t2;
  return t;
}

node *Insert2(node *t, int num, char ch)
{
  if (t==nul)
  {
    t=(node*) malloc(sizeof(node));
    t->l=t->r=nul;
    t->k=rand();
    t->ch=ch;
    t->size=1;
    return t;
  }
  else
  {
    t->size++;
    t->r=Insert2(t->r, num, ch);
    if (t->r->k>t->k) t=Left_Rotate(t);
    return t;
  }
}

node *Insert(node *t, int num, char ch)
{
  if (t==nul) return Insert2(t, num, ch);
  if (t->l->size>=num)
  {
    t->size++;
    t->l=Insert(t->l, num, ch);
    if (t->l->k>t->k) t = Right_Rotate(t);
    return t;
  }
  else
    if (t->l->size+1<num)
    {
      t->size++;
      t->r=Insert(t->r, num-t->l->size-1, ch);
      if (t->r->k>t->k) t=Left_Rotate(t);
      return t;
    }
    else
    {
      t->size++;
      t->l=Insert2(t->l, num, ch);
      return t;
    }
}

node *Delete(node *t)
{
  if (t->l==t->r&&t->r==nul)
  {
    free(t);
    return nul;
  }
  else
    if (t->l->k>t->r->k)
    {
      t->size--;
      t=Right_Rotate(t);
      t->r=Delete(t->r);
      return t;
    }
    else
    {
      t->size--;
      t=Left_Rotate(t);
      t->l=Delete(t->l);
      return t;
    }
}

void Clear(node *t)
{
  if (t==nul) return;
  Clear(t->l);
  Clear(t->r);
  free(t);
}

node *Delete(node *t, int st, int ed)
{
  if (st>ed) return t;
  if (t->l->size+1<st)
  {
    t->size-=(ed-st+1);
    t->r=Delete(t->r, st-(t->l->size+1), ed-(t->l->size+1));
    return t;
  }
  else
    if (t->l->size+1==st)
    {
      t=Delete(t);
      return Delete(t, st, ed-1);
    }
    else
      if (st==1&&ed==t->size)
      {
        Clear(t);
        return nul;
      }
      else
        if (ed<=t->l->size)
        {
          t->size-=ed-st+1;
          t->l=Delete(t->l, st, ed);
          return t;
        }
        else
        {
          t->size-=ed-st;
          int l=t->l->size-st+1;
          t->l=Delete(t->l, st, t->l->size);
          t->r=Delete(t->r, 1, (ed-st+1)-l-1);
          t=Delete(t);
          return t;
        }
}

void Print(node *t, int st, int ed)
{
  if (st>ed) return;
  if (t==nul) return;
  if (st>t->l->size+1) Print(t->r, st-(t->l->size+1), ed-(t->l->size+1));
  else
    if (st==t->l->size+1)
    {
      fprintf(out, "%c", t->ch);
      Print(t->r, 1, ed-st);
    }
    else
      if (ed<=t->l->size) Print(t->l, st, ed);
      else
        if (ed==t->l->size+1)
        {
          Print(t->l, st, ed-1);
          fprintf(out, "%c", t->ch);
        }
        else
        {
          Print(t->l, st, t->l->size);
          fprintf(out, "%c", t->ch);
          Print(t->r, 1, (ed-st+1)-(t->l->size-st+1)-1);
        }
}

int main()
{
  int lines;
  fscanf(in, "%d\n", &lines);
  nul = (node*) malloc(sizeof(node));
  nul->l=nul->r=nul;
  nul->k=-RAND_MAX;
  nul->ch=0;
  nul->size=0;
  t=nul;
  //initialize
  for (int i=0;i<lines;i++)
  {
    char ch,ch2;
    int num;
    char s1[200];
    ch=fgetc(in);
    ch2=fgetc(in);
    while (ch2!='\n'&&ch2!=' ') ch2=fgetc(in);
    if (ch!='P'&&ch!='N') fscanf(in, "%d", &num);
    if (ch2!='\n') fgetc(in);
    switch (ch)
    {
    case 'M':
      cursor=num+1;
      break;
    case 'I':
      for (int j=0;j<num;j++)
      {
        char ch3;
        ch3=fgetc(in);
        while (ch3=='\n') ch3=fgetc(in);
        t=Insert(t, cursor+j, ch3);
      }
      fgetc(in);
      break;
    case 'D':
      t=Delete(t, cursor, cursor+num-1);
      break;
    case 'G':
      Print(t, cursor, cursor+num-1);
      fprintf(out, "\n");
      break;
    case 'P':
      cursor--;
      break;
    case 'N':
      cursor++;
      break;
    }
  }
}

ZJU1027, 1074, 1076, 1093, 1100

2008年6月13日 02:23

1027是典型的两个串的DP,其实就是LCS的增强版,LCS相当于题中的表只有相等的元素值为1,其余都是0。于是按照LCS的方程做就可以了。关键问题在于边界,一定要先处理好边界(也就是一开头就是空格)的情况,这样DP才对。

#include <cstdio>
#include <cstring>

const int table[5][5]=
  {{5, -1, -2, -1, -3},
  {-1, 5, -3, -2, -4},
  {-2, -3, 5, -2, -2},
  {-1, -2, -2, 5, -1},
  {-3, -4, -2, -1, 0}};

int cases, f[101][101];

inline int max(int a, int b)
{
  return a>b?a:b;
}

int main()
{
  FILE *in = fopen("z_1027.in", "r");
  fscanf(in, "%d", &cases);
  for (int casess=0;casess<cases;casess++)
  {
    int l[2];
    char s[2][101];
    memset(s, 0, sizeof(s));
    fscanf(in, "%d %s\n", &l[0], &s[0]);
    fscanf(in, "%d %s\n", &l[1], &s[1]);
    for (int j=0;j<2;j++)
      for (int i=l[j]-1;i>=0;i--)
        s[j][i+1]=s[j][i];
    for (int j=0;j<2;j++)
      for (int i=1;i<=l[j];i++)
        switch (s[j][i])
        {
        case 'A':
          s[j][i]=0;
          break;
        case 'C':
          s[j][i]=1;
          break;
        case 'G':
          s[j][i]=2;
          break;
        case 'T':
          s[j][i]=3;
          break;
        }
    memset(f, 0, sizeof(f));
    for (int i=1;i<=l[1];i++)
      f[0][i]=f[0][i-1] + table[4][s[1][i]];
    for (int i=1;i<=l[0];i++)
      f[i][0]=f[i-1][0] + table[s[0][i]][4];
    for (int i=1;i<=l[0];i++)
      for (int j=1;j<=l[1];j++)
      {
        f[i][j]=max(f[i-1][j-1]+table[s[0][i]][s[1][j]], f[i-1][j]+table[s[0][i]][4]);
        f[i][j]=max(f[i][j], f[i][j-1]+table[4][s[1][j]]);
      }
    printf("%d\n", f[l[0]][l[1]]);
  }
}

1074是最大子矩阵,这范围太小了,随便枚举就能过。如果范围大一些可以用悬线+单调栈来做。

#include <cstdio>

int s[100][100], n, sum, a[100][100];

inline int max(int a, int b)
{
  return a>b?a:b;
}

int getsum(int x1, int y1, int x2, int y2)
{
  if (x1!=0)
    if (y1!=0) return s[x2][y2] - (s[x1-1][y2]+s[x2][y1-1]-s[x1-1][y1-1]);
    else return s[x2][y2] - s[x1-1][y2];
  else
    if (y1!=0) return s[x2][y2] - s[x2][y1-1];
    else return a[0][0];
}

 
int main()
{
  FILE* in = fopen("z_1074.in", "r");
  fscanf(in, "%d", &n);
  for (int i=0;i<n;i++)
    for (int j=0;j<n;j++)
    {
      fscanf(in, "%d", &a[i][j]);
      if (i!=0)
        if (j!=0) s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
        else s[i][j] = s[i-1][0] + a[i][j];
      else
        if (j!=0) s[0][j] = s[0][j-1] + a[i][j];
        else s[0][0]=a[0][0];
    }
  for (int i1=0;i1<n;i1++)
    for (int j1=0;j1<n;j1++)
      for (int i2=i1;i2<n;i2++)
        for (int j2=j1;j2<n;j2++)
          sum = max(getsum(i1, j1, i2, j2), sum);
  printf("%d\n", sum);
}

1076是相当于找出最多的不相交区间,先按右端点排序,然后就是个LIS。可以枚举起点直接贪心选择也可以DP。这题目的关键似乎在按照右端点排序这一步,因为我原来按照左端点排序就WA,改成右端点就AC,真奇怪…我觉得是等价的阿…哦,还有一个要注意的是如果DP应该把数组初始值都设为1。程序就不贴了,太丑陋了。

1093是三维的LIS的加强,而且可以旋转。旋转处理起来比较麻烦,于是干脆就把所有立方体都转好了加到里头去,然后规定不能旋转。这样就是个一般的DP了,按照第一维排序然后就很容易写方程了。一些小细节就是要事先把三维排个序,然后规定一下长宽高分别是哪个维,这样判断起来也方便DP起来也方便。

//AC
#include <cstdio>
#include <cstring>
#include <cstdlib>

int block[100][3], blocknum, n, f[100], max=0;
int cmp(const void *a, const void *b)
{
  int aa[3], bb[3];
  memcpy(aa, (int *)a, sizeof(aa));
  memcpy(bb, (int *)b, sizeof(bb));
  return (bb[0]-aa[0]);
}

void swap(int &a, int &b)
{
  int t=a; a=b; b=t;
}

void insert(int a, int b, int c)
{
  blocknum++;
  block[blocknum][0]=a;
  block[blocknum][1]=b;
  block[blocknum][2]=c;
}

int main()
{
  FILE *in = fopen("z_1093.in", "r");
  int cases=0;
  while (1)
  {
    cases++;
    fscanf(in, "%d", &n);
    if (n==0) return 0;
    memset(block, 0, sizeof(block));
    blocknum=0;
    memset(block[0], 10, sizeof(block[0]));
    for (int i=0;i<n;i++)
    {
      int a, b, c;
      fscanf(in, "%d%d%d", &a, &b, &c);
      if (a>b) swap(a, b);
      if (b>c) swap(b, c);
      if (a>b) swap(a, b);
      insert(a, b, c);
      if (a!=b)
      {
        if (c!=a) insert(b, c, a);
        if (c!=b) insert(a, c, b);
      }
      else
        if (b!=c) insert(a, c, b);
    }
    qsort(block, blocknum+1, sizeof(block[0]), cmp);
    memset(f, 0, sizeof(f));
    max=0;
    for (int i=1;i<=blocknum;i++)
      for (int j=0;j<i;j++)
        if (block[i][0]<block[j][0]&&block[i][1]<block[j][1]&&f[i]<f[j]+block[i][2])
        {
          f[i] = f[j] + block[i][2];
          max = f[i]>max?f[i]:max;
        }
    printf("Case %d: maximum height = %d\n", cases, max);
  }
}
 

1100是个状态压缩DP。按列DP,用二进制数s表示最后一列的状态(是有石头还是没有),然后DFS往上头放。这复杂度是指数级的,不过n不大没关系。我觉得程序写得异常简洁,对于一贯代码累赘的我来说…

//AC  0.07s

#include <cstdio>
#include <cstring>

long long f[2][3000], w, h;

void dfs(int pos, int cur, int next, int org)
{
  if (cur==(1<<h)-1)
  {
    if (pos!=w) f[pos&1][next] += f[(pos-1)&1][org];
    else f[pos&1][cur] += f[(pos-1)&1][org];
    return;
  }
  for (int i=0;i<h;i++)
    if ((cur&(1<<i))==0)
    {
      //yoko
      if ((next&(1<<i))==0&&pos!=w) dfs(pos, cur+(1<<i), next+(1<<i), org);
      //tate
      if (i<h-1&&(cur&(1<<(i+1)))==0) dfs(pos, cur+(1<<i)+(1<<(i+1)), next, org);
      return;
    }
}

       
int main()
{
  FILE *in = fopen("z_1100.in", "r");
  while (1)
  {
    fscanf(in, "%d%d", &w, &h);
    if (w==0) return 0;
    memset(f, 0, sizeof(f));
    f[0][0]=1;
    for (int i=1;i<=w;i++)
    {
      memset(f[i&1], 0, sizeof(f[i&1]));
      for (int j=0;j<(1<<h);j++)
        if (f[(i-1)&1][j])
          dfs(i, j, 0, j);
    }
    printf("%lld\n", f[w&1][(1<<h)-1]);
  }
}