bzoj2612: [Poi2003]Sums
题意
解法
题目翻译成人话为:
\( a_1 * x_1 + a_2 * x_2 + a_3 * x_3 .... a_n * x_n == k\) 关于 \( x_i \) 有自然数解。
因为 \( a_1 * x_1 + a_2 * x_2 + a_3 * x_3 .... a_n * x_n == k\) 所以 \( a_1 * x_1 + a_2 * x_2 + a_3 * x_3 .... a_n * x_n == k + a_i \) 一定成立。
所以 若 \( k_1 < k_2 \&\& k_1\%a_i == k_2\%a_i\) 如果 \( k_1 \) 可被表示,则 \(k_2\) 可被表示。
所以我们只需要求出在 \(a_i\) 的剩余系中,每个能被表示的最小整数就可以了。
这时,我们便可以用最短路算法了。
用 \(dis[i]\) 表示模 \(a_i\) 的最小可行整数。那么我们开 \(a_i\) 个点的图, \( i \) 向 \((i+a_j)\%a_i\) 连一条 \(a_j\) 的边。求一次最短路就可以了。
注意不能将边存下,否则会炸空间。
代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <cctype> #include <queue> #include <vector> #define INF 2139062143 #define MAX 0x7ffffffffffffff #define del(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; template<typename T> inline void read(T&x) { x=0;T k=1;char c=getchar(); while(!isdigit(c)){if(c=='-')k=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k; } const int maxn=40000+5; int a[5005]; int n,k; int dis[maxn]; bool vis[maxn]; priority_queue< pair<int,int> > q; void dij() { for(int i=1;i<a[1];i++) dis[i]=INF; q.push(make_pair(0,0)); while(!q.empty()) { pair<int,int> node=q.top();q.pop(); int u=node.second,d=-node.first; if(vis[u]) continue; vis[u]=1; for(int i=2;i<=n;i++) { int v=(u+a[i])%a[1]; if(dis[v]>d+a[i]) { dis[v]=d+a[i]; if(!vis[v]) q.push(make_pair(-dis[v],v)); } } } } int main() { read(n); for(int i=1;i<=n;i++) read(a[i]); dij(); read(k); int b; for(int i=1;i<=k;i++) { read(b); if(dis[b%a[1]]<=b) printf("TAK\n"); else printf("NIE\n"); } return 0; }