【51nod1693】水群

  • 本文为博主原创,未经许可不得转载

这题非常玄妙啊

首先简化题目,题面的意思就是,当前有一个数s

操作1是s*=k代价为k,操作2是s–代价为1

求把s从1变到n的最小代价

考虑把问题转化为图论模型,从i→i-1连一条权值为1的边,i→i*k连一条权值为k的边

在图上跑最短路即可

但是这样的话边数太多,时间会炸

我们考虑优化建图,注意到如果y=xij,那么从x连向y的边就可以用x-xi-xi*j这条路径代替

所以我们对于每个点i,只连i→i*p(p为质数)的边,这和原图是等价的

但是这样做复杂度依然会炸,题解上介绍了一种玄学方法:

打一个每个点自己最短路上的上一个点的表

那么可以从表中发现,i→i-1的边不会连续出现4次以上

而且i→i*p的边只有当p<=13的时候才有意义

所以这样又可以删去很多边,于是就能通过此题

当然这道题的正解是记忆化搜索,但是我不会啊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<bits/stdc++.h>
#define FILE "read"
#define MAXN 2000010
using namespace std;
typedef pair<int,int>pii;
const int prime[10]={0,2,3,5,7,11,13};
const int cnt=6;
struct node{int y,next,v;}e[MAXN<<1];
int n,len,Link[MAXN],vis[MAXN],dis[MAXN];
priority_queue<pii,vector<pii>,greater<pii> >q;
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
void insert(int x,int y,int v){e[++len].next=Link[x];Link[x]=len;e[len].y=y;e[len].v=v;}
void pre(){
for(int i=1;i<=n;++i)
for(int j=1;j<=cnt&&prime[j]*i<=n+4;++j)
insert(i,i*prime[j],prime[j]);
for(int i=2;i<=n+4;++i) insert(i,i-1,1);
}
void dijkstra(){
memset(dis,10,sizeof(dis));
q.push(make_pair(0,1)); vis[1]=1; dis[1]=0;
while(!q.empty()){
int v=q.top().first,x=q.top().second; q.pop();
for(int i=Link[x];i;i=e[i].next){
if(dis[x]+e[i].v<dis[e[i].y]){
dis[e[i].y]=dis[x]+e[i].v;
if(!vis[e[i].y]) q.push(make_pair(dis[e[i].y],e[i].y)),vis[e[i].y]=1;
}
}
vis[x]=0;
}
printf("%d\n",dis[n]);
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
n=read(); pre(); dijkstra();
return 0;
}
文章目录
,