题目
题目描述
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[n];++i) {
for(int j=1;j<n;++j) {
add(i,(i+V[j])%V[n],V[j]);
}
}
Dijkstra();
if(dist[t%V[n]]==-1 || dist[t%V[n]]>t) puts("Impossible");
else puts("Possible");
}
signed main()
{
int T = read();
while(T--) work();
return 0;
}
/*
1
4 78
12
5
7
9
Possible
*/
总结
同余最短路的一般套路。