1. 程式人生 > 實用技巧 >P3489 [POI2009]WIE-Hexer

P3489 [POI2009]WIE-Hexer

P3489:https://www.luogu.com.cn/problem/P3489
bzoj1139:https://darkbzoj.tk/problem/1139

dij 分層圖最短路+狀態壓縮,把狀態大小上限 \(2^p-1\) 寫成了 \(2^{p-1}-1\),然後debug了將近一晚上。。。

由於 \(p\le 13\),而且當前能走哪些路不能走哪些,又和每一個劍的有無有關,所以我們用一個 13 為二進位制數來表示當前狀態下,每一個劍有沒有
\(dis_{i,S}\) 表示的就是從 \(1\)\(i\),且狀態為 \(S\) 所需的最短路程
答案就是 \(\min\{dis_{n,S},S< 2^p\}\)


這裡為了 dij 那裡處理起來方便 複製板子方便,直接將這兩維用一個數表示了,具體看 id 陣列

然後 dij 中的每當開始處理一個 \(u\),就用當前節點鐵匠的資訊,來更新 \(S\),枚舉出邊的時候在判斷一下當前這條邊能不能走就行了

應該還是挺簡單
話說這題是當時剛學 dij 的時候,連堆優化都還不會,就用 dij 的標籤搜進了這題,然後還碼了半天,發現思路不對就直接讓它進任務計劃吃灰了
然後這一年多以後才想起來回來做

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 1700005
#define M 6005
struct graph{
	int fir[N],nex[M],to[M],w[M],tot;
	inline void add(int u,int v,int z){
		to[++tot]=v;w[tot]=z;
		nex[tot]=fir[u];fir[u]=tot;
	}
}G;
int n,m,p,k;
int heap[N],size;
int dis[N],in[N];
int blacksmith[N],monster[M];
int id[205][9005];
int get_i[N],get_S[N];
inline void push(int x){
	heap[++size]=x;
	reg int i=size,fa;
	while(i>1){
		fa=i>>1;
		if(dis[heap[fa]]<=dis[heap[i]]) return;
		std::swap(heap[fa],heap[i]);i=fa;
	}
}
inline int pop(){
	int ret=heap[1];heap[1]=heap[size--];
	reg int i=1,ls,rs;
	while((i<<1)<=size){
		ls=i<<1;rs=ls|1;
		if(rs<=size&&dis[heap[rs]]<dis[heap[ls]]) ls=rs;
		if(dis[heap[i]]<=dis[heap[ls]]) break;
		std::swap(heap[ls],heap[i]);i=ls;
	}
	return ret;
}
inline void dij(){
	std::memset(dis,0x3f,sizeof dis);dis[id[1][blacksmith[1]]]=0;
	push(id[1][blacksmith[1]]);in[id[1][blacksmith[1]]]=1;
	reg int u,v,S;
	while(size){
		u=pop();in[u]=0;
		S=get_S[u];
		S|=blacksmith[get_i[u]];//能打造新的劍就一定造
		for(reg int i=G.fir[get_i[u]];i;i=G.nex[i]){
			if((S&monster[i])!=monster[i]) continue;
			v=id[G.to[i]][S];
//				printf("(%d %d)=> (%d %d)\n",u,v,get_i[u],get_i[v]);
			if(dis[v]>dis[u]+G.w[i]){
				dis[v]=dis[u]+G.w[i];
				if(!in[v]) push(v),in[v]=1;
			}
		}
	}
}
int main(){
	n=read();m=read();p=read();k=read();
	for(reg int w,num,i=1;i<=k;i++){
		w=read();num=read();
		while(num--) blacksmith[w]|=(1<<(read()-1));
	}
	for(reg int i=1,u,v,w,num;i<=m;i++){
		u=read();v=read();w=read();num=read();
		G.add(u,v,w);G.add(v,u,w);
		while(num--){
			u=read();
			monster[G.tot]|=(1<<(u-1));
			monster[G.tot-1]|=(1<<(u-1));
		}
	}
	int id_=0;
	for(reg int i=1;i<=n;i++)
		for(reg int S=0;S<(1<<p);S++)
			id[i][S]=++id_,get_i[id_]=i,get_S[id_]=S;
	dij();
	int ans=1e9;
	for(reg int S=0;S<(1<<p);S++) ans=std::min(ans,dis[id[n][S]]);
	printf("%d",ans==1e9?-1:ans);
	return 0;
}