传送门
解题思路
先吐槽一下某谷翻译能不能把输入格式也翻译一下,感觉输入格式比题目描述都长。。
观察到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;
}