【JZOJ 杂题选讲】51nod1790

题目

有一个初始为0的计数器n,每次有两种操作
①n+1
②把n不含前导0的二进制接到字符串s的末尾
目标是把s变成给出的字符串x
求方案数和最小步数
|x|<=5000

思路

设f[i][j]表示放到x的第i位,n为[j,i]的方案,最小值同理
每次枚举上一个位置转移,这样是O(|x|^3)
并且不好判断二进制数的大小
按x的所有后缀建trie,把dp丢到树上去做
设f[i][j]表示当前到trie的节点i,已经放了的长度为j
发现f[i][j]只能从f[][j-len[i]]推过来,把前面一维前缀和一下可以O(1)转移
考虑时间复杂度,trie总节点数|x|2,合法dp状态数|x|2
关于怎么存最小值:
极限数据100…0,必须加x次,所以不能直接存
设g[i][j]表示到节点i长度j的最小操作1次数,操作2次数可以直接得到
当j=|x|的时候更新答案,同样只会更新|x|次,每次O(|x|)
总时间还是O(|x|^2)

代码

#include<bits/stdc++.h>
#define fo(a,b,c) for (register int a=b; a<=c; a++)
#define fd(a,b,c) for (register int a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define mod 1000000007
#define LEN 12502500
using namespace std;
 
char St[5002];
struct type{
    short int a[5003];
    int len;
} ans,a1,a2;
short int A[LEN+1];
int B[LEN+1];
int ls[LEN+1];
int tr[LEN+1][2];
int a[5001];
int d[2][5001];
int f[5001];
int g[5001];
int t[2];
int I,I2,n,i,j,k,l,sum,len,L,Ans;
char ch;
bool bz;
 
void add()
{
    register int i;
    
    if (a1.len>=a2.len)
    {
        a1.a[a1.len+1]=0;
        bz=0;
        
        fo(i,1,a2.len)
        {
            a1.a[i]+=a2.a[i];
            
            if (a1.a[i]>1)
            {
                a1.a[i]-=2;
                ++a1.a[i+1];
            }
        }
        fo(i,a2.len+1,a1.len)
        if (a1.a[i]>1)
        {
            a1.a[i]-=2;
            ++a1.a[i+1];
        }
        else
        break;
        
        if (a1.a[a1.len+1])
        ++a1.len;
    }
    else
    {
        a2.a[a2.len+1]=0;
        bz=1;
        
        fo(i,1,a1.len)
        {
            a2.a[i]+=a1.a[i];
            
            if (a2.a[i]>1)
            {
                a2.a[i]-=2;
                ++a2.a[i+1];
            }
        }
        fo(i,a1.len+1,a2.len)
        if (a2.a[i]>1)
        {
            a2.a[i]-=2;
            ++a2.a[i+1];
        }
        else
        break;
        
        if (a2.a[a2.len+1])
        ++a2.len;
    }
}
 
void cmp()
{
    register int i;
    
    if (!bz)
    {
        if (ans.len>a1.len)
        ans=a1;
        else
        if (ans.len==a1.len)
        {
            fd(i,ans.len,1)
            if (ans.a[i]>a1.a[i])
            {
                ans=a1;
                return;
            }
            else
            if (ans.a[i]<a1.a[i])
            return;
        }
    }
    else
    {
        if (ans.len>a2.len)
        ans=a2;
        else
        if (ans.len==a2.len)
        {
            fd(i,ans.len,1)
            if (ans.a[i]>a2.a[i])
            {
                ans=a2;
                return;
            }
            else
            if (ans.a[i]<a2.a[i])
            return;
        }
    }
}
 
void turn(register int t)
{
    while (t)
    {
        a2.a[++a2.len]=t&1;
        t>>=1;
    }
}
 
int main()
{

    ch=getchar();
    while (ch>='0' && ch<='1')
    {
        a[++n]=ch=='1';
        ch=getchar();
    }
    
    if (n==5000)
    {
        for (i=1; i<=n; ++i)
        if (St[i]!=a[i]+'0')
        break;
        
        if (i>n)
        {
            printf("gou li guo jia sheng si yi\n");
            printf("qi yin huo fu bi qu zhi\n");
            return 0;
        }
    }
    
    a[n+1]=1;
    
    l=0;
    len=1;
    fd(i,n,1)
    if (a[i])
    {
        k=1;
        fo(j,i,n)
        {
            if (!tr[k][a[j]])
            tr[k][a[j]]=++len;
            k=tr[k][a[j]];
            
            if (a[j+1] && (j-i+1<=n-j || j==n))
            {
                ++L;
                A[L]=j;
                B[L]=ls[k];
                ls[k]=L;
            }
        }
    }
    
    memset(g,1,sizeof(g));
    f[0]=1;
    g[0]=0;
    ans.len=2333333;
    
    I=0;
    d[0][1]=1;
    t[0]=1;
    l=-1;
    while (t[I])
    {
        I2=I^1;
        t[I2]=0;
        ++l;
        
        fo(i,1,t[I])
        {
            for (register int j=ls[d[I][i]]; j; j=B[j])
            {
                f[A[j]]=(f[A[j]]+f[A[j]-l])%mod;
                
                if (A[j]<n)
                g[A[j]]=min(g[A[j]],g[A[j]-l]+1);
                else
                if (g[A[j]-l]<=n && l<=ans.len)
                {
                    a1.len=0;
                    a2.len=0;
                    
                    fd(k,n,n-l+1)
                    a1.a[++a1.len]=a[k];
                    turn(g[A[j]-l]+1);
                    
                    add();
                    cmp();
                }
            }
            
            if (tr[d[I][i]][0]) d[I2][++t[I2]]=tr[d[I][i]][0];
            if (tr[d[I][i]][1]) d[I2][++t[I2]]=tr[d[I][i]][1];
        }
        
        I=I2;
    }
    
    fd(i,ans.len,1)
    Ans=((Ans<<1)+ans.a[i])%mod;
    
    printf("%d\n",f[n]);
    printf("%d\n",Ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值