洛谷 P3489 [POI2009]WIE-Hexer(二进制状压,分层图最短路)

传送门


解题思路

先吐槽一下某谷翻译能不能把输入格式也翻译一下,感觉输入格式比题目描述都长。。


观察到p非常小,于是考虑状态压缩,将当前能打的怪物压缩成一个二进制数。

按照每个二进制数分层,一共分得 2^13=8192 层。

边全部存下会MLE,所以考虑在转移的时候判断一下,只有出边的状态是现在状态的子集时才进行转移。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
const int maxn=2e6+5;
const int maxm=1e4+5;
int cnt,n,m,k,p[maxn],P,dis[maxn],value[maxn];
struct node{
	int v,next,w,f;
}e[maxm];
void insert(int u,int v,int w,int f){
	cnt++;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].f=f;
	e[cnt].next=p[u];
	p[u]=cnt;
}
void dij(){
	set<pair<int,int> > s;
	dis[value[0]*n]=0;
	s.insert(make_pair(0,value[0]*n));
	while(!s.empty()){
		int id=s.begin()->second;
		s.erase(s.begin());
		int state=id/n;
		int u=id%n;
		if(u==n-1){
			cout<<dis[id]<<endl;
			return;
		}
		for(int i=p[u];i!=-1;i=e[i].next){
			if((e[i].f&state)!=e[i].f) continue;
			int v=e[i].v;
			v=(state|value[v])*n+v;
			if(dis[v]>dis[id]+e[i].w){
				s.erase(make_pair(dis[v],v));
				dis[v]=dis[id]+e[i].w;
				s.insert(make_pair(dis[v],v));
			}
		}
	}
	cout<<-1;
}
template<class T>inline void read(T &x)
{
    x=0;register char c=getchar();register bool f=0;
    while(!isdigit(c))f^=c=='-',c=getchar();
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
}
int main(){
	ios::sync_with_stdio(false);
	memset(p,-1,sizeof(p));
	memset(dis,0x3f,sizeof(dis));
	read(n);read(m);read(P);read(k);
	for(int i=1;i<=k;i++){
		int w,q;
		read(w);read(q);
		w--;
		while(q--){
			int k;
			read(k);
			value[w]|=(1<<(k-1));
		}
	}
	for(int i=1;i<=m;i++){
		int u,v,w,q,res=0;
		read(u);read(v);read(w);read(q);
		u--;v--;
		while(q--){
			int x;
			read(x);
			res|=(1<<(x-1));
		}
		insert(u,v,w,res);
		insert(v,u,w,res);
	}
	dij();
	return 0;
}
posted @ 2021-09-28 17:45  尹昱钦  阅读(57)  评论(0编辑  收藏  举报