P6399 [COI2008] TAMNICA

· · 题解

最短路题目

分两个步骤

1.推导两侧的墙的标号。

2.转化成最短路

先看第一步

显然有规律,即每一圈对应一个完全平方数

接下来处理方法不唯一,以我的为例:

若以偶数的平方为一圈,令该偶数为k,墙对面数字为j .

k=4时可划分:

显然可以得知当前数字n\neq 4n\ne k*k时都有:

该部分: ```cpp long long finda(long long g) { k=sqrt(g); if(k%2==1) k--; if(g==4) return 1; if(g==k*k) return (k-2)*(k-2)+1; h=(g-k*k)/(k+1); h*=2; j=(k-2)*(k-2)+(g-k*k-1)-h; return j; } ``` --- 第二步: 对于打洞的部分可以理解为两点间有一条为1的边。 因为所有相邻点之间距离都为1, ![草图](https://cdn.luogu.com.cn/upload/image_hosting/dwxn9n1j.png) 那么走洞旁的点(及终点)一定最优。 因为$n$很大,所以用$map$ 离散来存。 --- 代码: ```cpp #include<bits/stdc++.h> #define For(i,a,b) for(register int i=a;i<=b;i++) #define Rep(i,a,b) for(register int i=a;i>=b;i--) using namespace std; inline long long read() { char c=getchar();long long x=0;bool f=0; while(!isdigit(c))f^=!(c^45),c=getchar(); while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar(); if(f)x=-x;return x; } const int maxn=100010; const int maxm=200010; struct edge{int next,v;long long w;}e[3*maxm]; map<long long,int>MP; int head[maxm],cnt; long long n,dis[maxm],d,w; int m,u,v,s,pos; long long a[maxn],b[maxn]; long long p[maxm]; struct node { int u; long long d; bool operator <(const node& r) const{return d>r.d;} }; node Q; void add(int u,int v,long long w) { e[++cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; } priority_queue<node>q; long long g,h,j,k; long long finda(long long g) { k=sqrt(g); if(k%2==1) k--; if(g==4) return 1; if(g==k*k) return (k-2)*(k-2)+1; h=(g-k*k)/(k+1); h*=2; j=(k-2)*(k-2)+(g-k*k-1)-h; return j; } int main() { memset(dis,0x3f,sizeof dis); n=read();scanf("%d",&m);//m=read(); MP[1]=1;p[++pos]=1; MP[n]=1;p[++pos]=n; For(i,1,m) { a[i]=read();b[i]=finda(a[i]); if(!MP[a[i]]) { MP[a[i]]=1; p[++pos]=a[i]; } if(!MP[b[i]]) { MP[b[i]]=1; p[++pos]=b[i]; } } sort(p+1,p+pos+1); For(i,1,pos) { MP[p[i]]=i; } For(i,1,pos-1) { add(i,i+1,p[i+1]-p[i]); add(i+1,i,p[i+1]-p[i]); } For(i,1,m) { add(MP[a[i]],MP[b[i]],1ll); add(MP[b[i]],MP[a[i]],1ll); } dis[1]=0; q.push((node){1,0}); while(!q.empty()) { Q=q.top(); q.pop(); u=Q.u;d=Q.d; if(d!=dis[u]) continue; for(int i=head[u];i;i=e[i].next) { v=e[i].v;w=e[i].w; if(dis[u]+w<dis[v]) { dis[v]=dis[u]+w; q.push((node){v,dis[v]}); } } } printf("%lld",dis[MP[n]]); } ```