[noi.ac 模擬賽9] A.出征準備(同餘最短路)
阿新 • • 發佈:2020-11-28
題目
題目描述
dkw 的劍需要 \(t\) 點能量, 黑市有 \(n\) 種充能水晶, 每種充能水晶有它的能量值 \(V_i\) 。
dkw 有無限的錢, 可以買任意種水晶任意個(不能買負數個)。
由於 dkw 的劍必須剛好充滿能量才能使用,所以你需要回答他是否存在一種方案,剛好充滿 dkw 的劍的能量。
輸入格式
第一行是一個 \(T\) , 代表測試資料組數
對於每組測試資料, 第一行有兩個整數 \(n, t\)
接下來 \(n\) 行, 每行一個整數, \(V_i\)
輸出格式
對於每組測試資料, 如果能, 輸出 Possible ; 否則輸出 Impossible
資料範圍
\(1≤T≤10, 1≤n≤50, 1≤t≤10^{18}, 0≤V_i≤10^4\)
題解
令 \(V_1\) 是 \(\{V\}\) 中最小的。設 \(dist[i]\) 表示經過 \(V_2,...,V_n\) 的加法,最小的 \(p \equiv i \pmod{V_1}\)。跑一個同餘最短路就好了。
記 \(m=Vn\),時間複雜度 \(O(Tm \log m)\)
程式碼
Talk is cheap.Show me the code.
#include<bits/stdc++.h> #define int long long #define mp make_pair #define fi first #define se second using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); } return x * f; } typedef pair<int,int> PII; const int N = 1e4+7; int t,n,cnt; int V[N],head[N]; struct Edge { int next,to,w; }edge[N*50]; inline void add(int u,int v,int w) { edge[++cnt] = (Edge)<%head[u],v,w%>; head[u] = cnt; } int dist[N]; //dist[i] 最小的 p≡i(mod V[1]) bool vis[N]; void Dijkstra() { memset(dist, -1, sizeof(dist)); memset(vis, 0, sizeof(vis)); priority_queue<PII> q; dist[0] = 0; q.push(mp(-dist[0],0)); while(!q.empty()) { int u = q.top().se; q.pop(); if(vis[u]) continue; vis[u] = 1; for(int i=head[u];i;i=edge[i].next) { int v = edge[i].to, w = edge[i].w; if(dist[u]+w > t) continue; if(dist[v]==-1 || dist[v]>dist[u]+w) { dist[v] = dist[u] + w; q.push(mp(-dist[v],v)); } } } } void work() { memset(head, 0, sizeof(head)); cnt = 0; n = read(), t = read(); for(int i=1;i<=n;++i) V[i] = read(); sort(V+1,V+1+n); for(int i=0;i<V[1];++i) { for(int j=2;j<=n;++j) { add(i,(i+V[j])%V[1],V[j]); } } Dijkstra(); if(dist[t%V[1]]==-1 || dist[t%V[1]]>t) puts("Impossible"); else puts("Possible"); } signed main() { int T = read(); while(T--) work(); return 0; } /* 1 4 78 12 5 7 9 Possible */
總結
同餘最短路的一般套路。