【省选模拟】—River(贪心)

传送门

很显然可以发现对于某一天,一定是选择到达下一步所用时间最小的那一天
也就是在 p p p的决策是选择 i + a i − p i+a_i-p i+aip最小的一天
而且对于一个 p p p决策一定是固定的
对每一天处理一个下步走多少天的 n x t nxt nxt

那在 n ≤ 1 e 6 n\le 1e6 n1e6就可以简单解决了

inline ll calc(int p=1,int k=n){
	ll res=0;
	for(int i=1;i<=k;i++){
		int x=(p-1)%m+1;
		p=p+nxt[x],res+=nxt[x];
	}
	return res;
}

结果处理 n x t nxt nxt的时候自己手贱把 n , m n,m nm写反了233

n ≤ 1 e 9 n\le 1e9 n1e9的时候

因为 m m m只有 1 e 6 1e6 1e6,所以路径一定是一个 ρ \rho ρ一样的
只需要找到环然后记一下尾巴乱搞一下就可以了

结果意识模糊各种细节出锅233

复杂度 O ( n ) O(n) O(n),不知道快到哪里去了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const int RLEN=1<<18|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=gc();}
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return res*f;
}
const int N=2000005;
int n,m;
ll f[N],nxt[N<<1],cir,ans;
inline void init(){
	nxt[m<<1]=1e18;
	for(int i=(m<<1)-1;~i;--i)nxt[i]=min(nxt[i],nxt[i+1]+1);
}
int dep[N],prelen,len,tot;
void dfs(int u){
	int v=(nxt[u]+u)%m;
	if(dep[v]){
		len=dep[u]-dep[v]+1,prelen=dep[v];
		cir=f[dep[u]]-f[dep[v]]+nxt[u];return;
	}
	dep[v]=++tot;
	f[tot]=f[tot-1]+nxt[u];
	dfs(v);
}
signed main(){
	n=read(),m=read();
	for(int i=0;i<m;i++)nxt[i]=nxt[i+m]=read();
	init();dep[0]=++tot;
	if(n<=tot){
		while(n--)ans+=f[ans%m];cout<<ans;return 0;
	}
	dfs(0);
	n-=prelen-1,ans+=f[prelen];
	ans+=1ll*(n/len)*cir;
	n%=len;
	while(n--)ans+=nxt[ans%m];
	cout<<ans;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值