洛谷P8060 [POI2003] Sums 题解
题目链接:P8060 [POI2003] Sums
题目描述:
我们给定一个整数集合 \(A\)。考虑一个非负整数集合 \(A'\),所有属于 \(A'\) 的集合的数 \(x\) 满足当且仅当能被表示成一些属于 \(A\) 的元素的和(数字可重复)。
比如,当 \(A = \{2,5,7\}\),属于 \(A'\) 的数为:\(0\)(\(0\) 个元素的和),\(2\),\(4\)(\(2 + 2\))和 \(12\)(\(5 + 7\) or \(7 + 5\) or \(2 + 2 + 2 + 2 + 2 + 2\));但是元素 \(1\) 和 \(3\) 不属于 \(A'\)。
输入格式:
第一行有一个整数 \(n\),代表集合 \(A\) 的元素个数。接下来每行一个数 \(a_i\) 描述一个元素。\(A = \{a_1,a_2,...,a_n\}\)。
接下来一个整数 \(k\),然后每行一个整数,分别代表 \(b_1,b_2,...,b_k\)。
输出格式:
输出 \(k\) 行。如果 \(b_i\) 属于 \(A'\),第 \(i\) 行打印
TAK
,否则打印NIE
。数据范围:
对于所有数据,\(1 \le n \le 5 \times 10^3\),\(1 \le k \le 10^4\),\(1 \le a_1 < a_2 < ... < a_n \le 5 \times 10^4\),\(0 \le b_i \le 10^9\)。
考虑随便取一个模数 \(a_i\) ,建议从小到大排序后选 \(a_1\)
然后求出 凑出每个 \(x \in [0,a_1)\) 的所需的最小代价
或者说,用最少的 \(a_i\) 凑出一个模 \(a_1\) 意义下的 \(x\) ,记这个数为 \(d_x\)
而同余最短路干的事情就是:求模 \(M\) 余 \(x\) 的最小基数 \(d_x\)
这样对于每个询问,我们只要看看 \(d_{k\,\bmod\, a_1}\) 是不是小于 \(k\) 就可以了
时间复杂度 \(O(n a_i \log a_i)\)
代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
#include <queue>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(5e4+15)
#define M (int)(5e5+15)
int n,Q,a[N],d[M];
struct node{int u,dis;};
bool operator<(node a,node b){return a.dis>b.dis;}
priority_queue<node> q;
void dijkstra()
{
for(int i=0; i<a[1]; i++) d[i]=INF;
d[0]=0; q.push({0,0});
while(!q.empty())
{
int u=q.top().u,dis=q.top().dis; q.pop();
if(dis!=d[u]) continue; // 一个点可能被入队多次
for(int i=2; i<=n; i++)
{
int v=(u+a[i])%a[1];
if(d[v]>d[u]+a[i])
{
d[v]=d[u]+a[i];
q.push({v,d[v]});
}
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n;
for(int i=1; i<=n; i++)
cin >> a[i];
sort(a+1,a+1+n); dijkstra();
cin >> Q;
for(int x; Q--;)
{
cin >> x;
if(d[x%a[1]]<=x) cout << "TAK\n";
else cout << "NIE\n";
}
return 0;
}