APIO2014题解

27 篇文章 0 订阅
8 篇文章 0 订阅

话说就要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
        
        
       
       
      
      
     
     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值