蒟蒻养成记——奇妙的构图+记忆化搜索

Program

【题目描述】

你有一个正整数序列{Ai},大小为n,你还有2个变量x,y

执行以下程序:

x=1,y=0

While(true)

x+=Ax,y+=Ax

If x<=0 orx>n then exit

x-=Ax,y+=Ax

If x<=0 orx>n then exit

 

你的任务是得到一个序列A2,A3...An,

枚举i(1<=i<=n-1),令A1等于i,输出上面那个程序y的值,如果程序陷入死循环输出-1

【输入格式】

第一行n

第二行n-1个整数a2,a3,...,an

【输出格式】

输出n-1行,为枚举每个i得到的结果

【输入输出样例】

Input1

4
2 4 1

Output1

3
6
8

Input2

3
1 2

Output2

-1
-1

【样例解释】

In the firstsample

1        For i = 1, xbecomes  and ybecomes 1 + 2 = 3.

2        For i = 2, xbecomes  and ybecomes 2 + 4 = 6.

3        For i = 3, xbecomes  and ybecomes 3 + 1 + 4 = 8.

 

【数据约定】

40%数据 n<=1000

100%数据2<=n<=10^5 1<=ai<=10^9


【解法一】

想到 循环到某个数后,后面出现的数一定相同。所以就想到构图,如果i点能连接连接到j点,则用i连接到j点。并且把j的值传递给i值,这样,到一个点后,我们就能快速知道到结束时所输出的值为多少,整体时间复杂度约为 o(n)


【代码一】

#include<cstdlib>
#include<cstdio>
#include<iomanip>
#include<iostream>
#include<cstring>
using namespace std;
int i,j,k,m,n,o,p,js,jl,kk;
struct tu
{
int pp;
int pj;
};
int a[100001];
int b[100001]={0};
tu f[100001];
int build(int k,int u)
{
if(u==0)if(f[k].pp!=0)return(f[k].pp);
if(u==1)if(f[k].pj!=0)return(f[k].pj);
if(b[k]==1)
{
return(-1);
}
b[k]=1;
if(u==0)
{
   if(k+a[k]>n)
   {
    f[k].pp=a[k];
    return(a[k]);
   }
   else
   {
    kk=build(k+a[k],1);
    if(kk==-1)f[k].pp=-1;
    else f[k].pp=kk+a[k];
   }
   return(f[k].pp);
    }
    
    else
    {
   if(k-a[k]<=0)
   {
    f[k].pj=a[k];
    return(a[k]);
   }

   else
   {
    kk=build(k-a[k],0);
    if(kk==-1)f[k].pj=-1;
    else f[k].pj=kk+a[k];
   }
   return(f[k].pj);
    }
}




int main()
{
FILE *fin,*fout;
fin=fopen("program.in","rb");
fout=fopen("program.out","wb");
fscanf(fin,"%d",&n);
for(int i=2;i<=n;i++)fscanf(fin,"%d",&a[i]);
for(i=2;i<=n;i++)
{
k=i;
b[a[i]]=1;
memset(b,0,sizeof(b));
build(k,0);
memset(b,0,sizeof(b));
build(k,1);
}
for(int i=1;i<=n-1;i++)
{
if(f[1+i].pj>0)fprintf(fout,"%d\n",f[1+i].pj+i);
else fprintf(fout,"-1\n");
}
fclose(fin);
fclose(fout);
}


【解法二】

一个点按是加一个数得到还是减一个数得到的性质拆成2份,这样发现一个点最多一个出度

那么就形成了若干颗树和若干个环。枚举A1=i连出去的情况,是在树上走还是走到环里。记忆化搜索即可。


【代码二】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<ctime>
using namespace std;
#define REP(i,n) for(i=0;i<n;++i)
#define FOR(i,j,k) for(i=j;i<=k;++i)
#define FORD(i,j,k) for(i=j;i>=k;--i)
#define met(i,j) memset(i,j,sizeof(i))
#define fo(i,j) for(typeof(j.begin()) i=j.begin();i!=j.end();++i)
#define PB push_back
#define MK make_pair
#define sz size()
const int inf=2147483647;
typedef pair<int, int> PII;
typedef long long LL;
typedef unsigned long long ULL;
FILE*fin,*fout;
#define N 400005
int bj,n,a[N],mark[N];
LL f[N];


void init()
{
    int i;
    fscanf(fin,"%d",&n);
    FOR(i,2,n)
    {
        fscanf(fin,"%d",a+i);
    }
}


LL dfs(int v)
{
    int _x,x;LL temp;
    if(mark[v]==1)return f[v]=-1;
    if(v==1){bj=1;return 0;}
    if(f[v]!=0)return f[v];
    mark[v]=1;
    _x=x=v/2+1;
    if(v%2==0)
    {
        x+=a[x];
        if(x<=0||x>n)temp=0;else temp=dfs(x*2-1);
    }else
    {
        x-=a[x];
        if(x<=0||x>n)temp=0;else temp=dfs(x*2-2);
        
    }
    mark[v]=0;
    if(temp==-1)return f[v]=-1;else return f[v]=temp+a[_x];
}


void work()
{
    int i;LL temp;
    FOR(i,1,n-1)
    {
        f[0]=f[1]=0;
        a[1]=i;bj=0;
        temp=dfs(0);
        if(bj)temp+=dfs(1);
        fprintf(fout,"%I64d\n",temp);
    }
}


int main()
{
fin=fopen("program.in","rb");
fout=fopen("program.out","wb");
    init();
    work();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值