题解 P2438 【[SDOI2005]解环】

· · 题解

一道找规律的好题【雾

在只有一个环n的时候,我们要拆下来这个环,就要装上环n-1,拆掉n,再拆掉n-1;

装上和拆掉一个环的步数是相同的。设t(n)表示对一个环n进行操作(拆下或者安上)需要的步数,可以得出t(n)=2*t(n-1)+1,t(1)=1

再归纳总结一下,t(n)=2^n -1;

再观察有两个环的情况:我们设有环a,b(a>b)被连上,在拆下a之前我们一定会安装b,而安装b所需要的步数是t(b),当b原本就被连接的情况下,我们就能从总步数中省下操作b的步数,所以总步数应为t(a)-t(b)。

观察有三个环的情况:设有环a,b,c(a>b>c)被连上,观察环a,b:根据上一条,原本的步数t(a)中省下了对b进行操作的步数,这里对b进行操作的步数本应为t(b),但是观察b,c我们发现:根据上一条,因为有了c的存在,导致此次对b的操作步数从t(b)变为了t(b)-t(c)。综合一下发现,对三个环a,b,c进行操作所需要的步数为t(a)-(t(b)-t(c))=t(a)-t(b)+t(c)。

对以上进行总结,能够推导出对于若干个环a,b,c,d,e,……(a>b>c>d>e>……)进行操作的步数应为t(a)-t(b)+t(c)-t(d)+t(e)……

预处理一下t数组,然后从后往前按照-和+的变化进行计算。

n<=1000,2^1000-1需要用高精度进行计算。

附代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct st{
    int a[1000];
    int len;
}t[1001],ans;
int f[1001],s[1001],n,x,m;
#define max(a,b) a>b?a:b
st rol(st x)
{
    st c=x;
    for(int i=c.len;i;i--)
    {
        c.a[i]<<=1;
        if(c.a[i]>9)
        c.a[i+1]++,c.a[i]-=10; 
    }
    if(c.a[c.len+1]) c.len++;
    return c;
}
st add(st x,st y)
{
    st c=x;
    c.len=max(c.len,y.len); 
    for(int i=1;i<=c.len;i++)
    {
        c.a[i]=c.a[i]+y.a[i];
        if(c.a[i]>9)
        c.a[i+1]++,c.a[i]-=10;
    }
    if(c.a[c.len+1]) c.len++;
    return c;
}
st sub(st x,st y)
{
    st c=x;
    for(int i=1;i<=c.len;i++)
    {
        c.a[i]-=y.a[i];
        if(c.a[i]<0)
        c.a[i]+=10,c.a[i+1]--;
    }
    while(!c.a[c.len]) c.len--;
    return c; 
}
void print(st x)
{
    for(int i=x.len;i>=1;i--)
    printf("%d",x.a[i]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[i]);
        if(f[i]) m=i;
    }
    x=2;
    for(int i=m;i;i--)
    if(f[i])
    {
        s[i]=x;
        x^=1;
    }
    t[1].len=t[1].a[1]=1;
    for(int i=2;i<=m;i++)
    {
        t[i]=rol(t[i-1]);
        t[i].a[1]++;
    }
    ans=t[m];
    for(int i=m-1;i;i--)
    {
        if(s[i]==2)
        ans=add(ans,t[i]);
        if(s[i]==3)
        ans=sub(ans,t[i]);
    }
    print(ans);
}