[SCOI2006]城堡
这种做法需要吸氧才能通过,不是正解,所以只是提供一种题解区还没有的思路。
可以用分层图的思路来做这道题。每次从一个方格转移到另一个方格时时间都发生了改变,时间的改变可以看成是不同层次的图之间的转移,所以只需要按照题意在相邻的两层图之间连边跑最短路即可。显然假如所有方块的周期的最小公倍数是 \(lcm\) ,那么所有位置相同、时间模这个公倍数相同的状态在转移上是完全一样的,可以看成是同一个状态,所以第 \(lcm-1\) 层图应该向第 \(0\) 层图连边。其它有些细节比如建立超级源点(它可以一直呆在最开始节点上不出去)和超级汇点方便敲代码就是分层图的基本操作了。
#include<bits/stdc++.h>
//#define feyn
#define p(a,b) (a*(m+2)+b+2)
const int N=3000010;
const int M=1010;
const int R=12;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int gcd(int s1,int s2){
return s2==0?s1:gcd(s2,s1%s2);
}
inline int lcm(int s1,int s2){
return s1*s2/gcd(s1,s2);
}
int m,lc=1,num[M],a[M][R];
struct edge{
int t,v,next;
}e[N*3];
int head[N],esum;
inline void add(int fr,int to,int val){
e[++esum]=(edge){to,val,head[fr]};head[fr]=esum;
}
int dis[N];
bool vis[N];
struct node{
int pl,dis;
};
inline bool operator <(node s1,node s2){
return s2.dis<s1.dis;
}
priority_queue<node>q;
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);
for(int i=1;i<=m;i++){
read(num[i]);lc=lcm(lc,num[i]);
for(int j=0;j<num[i];j++)read(a[i][j]);
}
num[0]=num[m+1]=1;
//0 to 1
//S to 0
//m+1 to T
for(int i=0;i<lc;i++){
add(0,p(i,0),0);
add(p(i,(m+1)),1,0);
int nt=(i+1)%lc;
add(p(i,0),p(nt,1),a[1][nt%num[1]]);
}
for(int i=1;i<=m;i++){
for(int t=0;t<lc;t++){
int nt=(t+1)%lc;
add(p(t,i),p(nt,i),a[i][nt%num[i]]);
add(p(t,i),p(nt,i+1),a[i+1][nt%num[i+1]]);
add(p(t,i),p(nt,i-1),a[i-1][nt%num[i-1]]);
}
}
memset(dis,0x3f,sizeof(dis));
dis[0]=0;q.push((node){0,0});
while(!q.empty()){
node now=q.top();q.pop();
int wh=now.pl,nd=now.dis;
if(vis[wh])continue;vis[wh]=true;
for(int i=head[wh],th;i;i=e[i].next){
int nval=dis[wh]+e[i].v;th=e[i].t;
if(nval<dis[th]){
dis[th]=nval;q.push((node){th,dis[th]});
}
}
}
printf("%d",dis[1]);
return 0;
}
一如既往,万事胜意