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;
}
View Code

 

posted @ 2018-10-24 22:10  Mr_asd  阅读(434)  评论(0编辑  收藏  举报