APIO2014题解

话说就要APIO2015了,我才把2014的题做完。。。。

1、回文串

当时考场上据说有很多人用Manacher+其他各种字符串利器虐了。。。

但是现在我们有了回文树这种裸题,这不是水吗。。。。。

#include 
#include 
#include 
 
using namespace std;
typedef long long LL;
const int Maxn=300005;
char S[Maxn];
int son[Maxn][26],fail[Maxn];
int cnt[Maxn],len[Maxn];
int n,st,i,x,cur,nw,last;
LL ans;
 
int newnode(int x){
  //for (i=0;i<26;i++)
  //  son[x][i]=0;
  len[st] = x;
  cnt[st] = 0;
  return st++;
}
 
void init(){
  gets(S+1);
  n=strlen(S+1);
  S[0]='#';
  newnode( 0 );
  newnode( -1 );
  fail[0] = 1;
  last = 0;
}
 
int get_fail(int x,int n){
  while ( S[n-len[x]-1]!=S[n] ) x=fail[x];
  return x;
}
 
int main(){
  //freopen("palindrome.in","r",stdin);
  //freopen("palindrome.out","w",stdout);
  init();
  for (i=1;i<=n;i++){
    x = S[i]-'a';
    cur = get_fail( last, i );
    if ( !son[cur][x] ){
      nw = newnode( len[cur]+2 );
      fail[nw] = son[ get_fail( fail[cur], i ) ][x];
      son[cur][x] = nw;
    }
    last = son[cur][x];
    cnt[ last ]++;
  }
  for (i=st-1;i>=0;i--)
    cnt[fail[i]]+=cnt[i];
  for (i=2;i
2、序列分割

可以发现最终分割形态确定后,无论什么样的分割顺序,分割出来的分数是一样的。

所以我们可以轻松地写出状转方程:f[i][j]=f[k][j]+sum[k]*(sum[i]-sum[k])

再把两个转移状态放在不等式两边华裔话就可以发现这是一个基本的单调性优化dp

#include 
#include 
#include 

using namespace std;
typedef long long LL;
LL sum[100005],f[100005][2];
int p[100005][205];
int x,n,K,i,j,j1,j2,q[100005],l,r;

void print(int i,int j){
  if (j==0) {printf("%d",i);return;}
  print(p[i][j],j-1);
  if (j==K) printf("\n");
    else printf(" %d",i);
}

LL calc1(int x)
{ return f[x][j2]+sum[x]*(sum[i]-sum[x]); }

bool calc2(int x1,int x2,int x3){
  if (sum[x2]==sum[x3]) return 1;
  return (f[x1][j2]-f[x2][j2]-sum[x1]*sum[x1]+sum[x2]*sum[x2])*(sum[x3]-sum[x2]) >=
  			(f[x2][j2]-f[x3][j2]-sum[x2]*sum[x2]+sum[x3]*sum[x3])*(sum[x2]-sum[x1]);
}

int main(){
  freopen("sequence.in","r",stdin);
  freopen("sequence.out","w",stdout);
  scanf("%d%d",&n,&K);
  for (i=1;i<=n;i++){
  	scanf("%d",&x);
  	sum[i] = sum[i-1]+x;
  }
  for (j=1;j<=K;j++){
  	j1 = j&1; j2 = j1^1;
  	q[l=r=1] = j;
  	for (i=j+1;i<=n;i++){
  	  while (l
3、连珠线

应该是14年难度最大的一题了吧。。。

题解的方法似乎和简单,跪跪跪跪跪!

还是说一说我的做法吧。

设状态f[i][0~4]

f[i][0]:表示i以Append的方式连入,连向其子树下的点(其实就是儿子或者孙子),这个子树i上的最大得分

f[i][1]:表示i以Append的方式连入,不连向其子树的点,这个子树上的最大得分

f[i][2]:表示i以Insert的方式连入,其父亲以Append的方式先连入i某个儿子,这个子树上的最大得分

f[i][3]:表示i以Insert的方式连入,其某个儿子以Append的方式先连入i父亲,这个子树上的最大得分

f[i][4]:表示i以Insert的方式连入,他的某两个儿子先以Append的方式连接,这个子树上的最大得分

这样就可以很轻松地写出状转方程(我乱说的,还是需要井然有序的列出可能,千万不可漏掉某种情况)

时间复杂度O(n)

#include 
#include 
#include 

using namespace std;
const int Maxn=200005, INF = 1000000000;
int node[Maxn<<1],next[Maxn<<1],len[Maxn<<1];
int f[Maxn][5],a[Maxn],fl[Maxn],q[Maxn],fa[Maxn];
int l,r,n,x,y,z,delta,d1,d2,t1,t2,de1,de2,te1,te2,tot,i;

void add(int x,int y,int z){
  node[++tot]=y; next[tot]=a[x]; a[x]=tot; len[tot]=z;
  node[++tot]=x; next[tot]=a[y]; a[y]=tot; len[tot]=z;
}

void bfs(){
  for (q[l=r=1]=1;l<=r;l++){
    for (i=a[q[l]];i;i=next[i])
    if (node[i]!=fa[q[l]]){
      fa[ q[++r]=node[i] ] = q[l];
      fl[q[r]] = len[i];
    }
  }
}

void dp(){
  for (;r>0;r--){
    x = q[r];
    // 0 连向子孙节点
    delta = -INF;
    for (i=a[x];i;i=next[i]){
      if ( (y=node[i]) == fa[x] ) continue;
      f[x][0] += max(f[y][1], f[y][2]);
      delta = max(delta, max(f[y][0], max(f[y][3], f[y][4]))-max(f[y][1], f[y][2]) );
    }
    f[x][0] += delta;
	f[x][0] = max(f[x][0], 0);
    // 1 连向非子孙节点
    for (i=a[x];i;i=next[i]){
      if ( (y=node[i]) == fa[x] ) continue;
      f[x][1] += max(f[y][1], f[y][2]);
    }
    // 2 插在儿子和父亲中
    delta = -INF;
    for (i=a[x];i;i=next[i]){
      if ( (y=node[i]) == fa[x] ) continue;
      f[x][2] += max(f[y][1], f[y][2]);
      delta = max(delta, f[y][1]+fl[x]+fl[y]-max(f[y][1], f[y][2]));
    }
    f[x][2] += delta;
    // 3 插在父亲和儿子中
    delta = -INF;
    for (i=a[x];i;i=next[i]){
      if ( (y=node[i]) == fa[x] ) continue;
      f[x][3] += max(f[y][1], f[y][2]);
      delta = max(delta, max(f[y][0],f[y][4])+fl[x]+fl[y]-max(f[y][1], f[y][2]));
    }
    f[x][3] += delta;
    // 4 插在两个儿子中
    d1 = -INF; d2 = -INF;
    t1 = -INF; t2 = -INF;
    for (i=a[x];i;i=next[i]){
      if ( (y=node[i]) == fa[x] ) continue;
      f[x][4] += max(f[y][1], f[y][2]);
      if (d1 < max(f[y][0],f[y][4])+fl[y]-max(f[y][1],f[y][2])){
        d2 = d1; de2 = de1;
        d1 = max(f[y][0],f[y][4])+fl[y]-max(f[y][1],f[y][2]);
        de1 = y;
      } else
      if (d2 < max(f[y][0],f[y][4])+fl[y]-max(f[y][1],f[y][2])){
        d2 = max(f[y][0],f[y][4])+fl[y]-max(f[y][1],f[y][2]);
        de2 = y;
      }
      if (t1 < f[y][1]+fl[y]-max(f[y][1],f[y][2])){
        t2 = t1; te2 = te1;
        t1 = f[y][1]+fl[y]-max(f[y][1],f[y][2]);
        te1 = y;
      } else
      if (t2 < f[y][1]+fl[y]-max(f[y][1],f[y][2])){
        t2 = f[y][1]+fl[y]-max(f[y][1],f[y][2]);
        te2 = y;
      }
    }
    if (d2==-INF || t2==-INF) {f[x][4]=-INF;continue;}
    if (de1==te1) f[x][4] += max(d1+t2, d2+t1);
      else f[x][4] += d1+t1;

  }
}

int main(){
  freopen("beads.in","r",stdin);
  freopen("beads.out","w",stdout);
  scanf("%d",&n);
  for (i=1;i

你可能感兴趣的:(dp&递推,日常,BZOJ)