牛场围栏(余数+最短路)

文章目录

(upd.2019.7.23 添加了关于gcd==1时有解的证明)

今天学了不少新东西,闲着无聊都发了两发博客了,明明这只是XXY的基础题

题意

用一堆整数S相加组成新数,每个可以用任意次数,求最大的不能组成的数。

思路

  • 首先判掉两种情况:1可以组成任何数,2不能组成的数无限大。

  • 第一种情况就是在原始的整数集合S中有1。第二种情况是S中所有数的GCD大于1,所以所有不能被GCD整除的数都无法组成

  • 然后终于到了求最大无法构成的数了。首先我们假设S中所有数存在l[]数组中,minl是l[]里的随便一个数。所以所有可以组成的数都可以%minl变成0~minl-1的一个数。然而这有什么用呢?我们可以记录一个dis[]数组,用来存能被组成%minl值为i的数中最小的那个,因为只要最小的那个能被组成了,在这个最小数基础上加上minl,就可以保证与他同于的比他大的数全都可以被组成。所以所有dis[]中存的最小值减去minl就是不能取到的最大值,把所有的取min就是最终答案。

  • 那么如何求最小值呢?可以联想到最短路算法 (根本联想不到,我第一次看到题解里的SPFA是懵逼的)。具体实现挺简单的,就像普通可行性DP一样转移就好了

upd.

条件:令 S = { x i ∣ 1 ≤ i ≤ n } S=\{x_i|1 \le i \le n\} S={xi1in}表示初始数集,该集合满足 1 ∈ ̸ S 1 \in\not S 1̸S g c d ( x 1 , ⋯   , x n ) = 1 gcd(x_1, \cdots,x_n) = 1 gcd(x1,,xn)=1

证明有解,分为以下两部分:

  1. 一定存在一个数无法构造出来
  2. 不能构造出来的数有上界

首先第1点,根据条件,1是无法构造出来的,所以必然有解。

第2点,根据上面说的算法,本题有解可以转化为存在一个 x k ∈ S x_k \in S xkS
使得 a 1 x 1 + a 2 x 2 + ⋯ + a n x n ≡ 1 ( m o d    x k ) , a i ∈ N a_1x_1+a_2x_2+\dots+a_nx_n \equiv 1(mod \; x_k),a_i \in N a1x1+a2x2++anxn1(modxk),aiN,既然可以做出1,那么对于每一个同余类只要在等式两边都乘上一个数就好了。

然后来解决转化之后的问题。根据裴蜀定理在多个数下的推论,
可以知道 a 1 x 1 + a 2 x 2 + ⋯ + a n x n = 1 a_1x_1+a_2x_2+\dots+a_nx_n=1 a1x1+a2x2++anxn=1有整数解。然后因为对 x k x_k xk取模,所以 a k ∈ Z a_k\in Z akZ
g c d ( x i ∣ i = ̸ k ) = g gcd(x_i|i=\not k)=g gcd(xii≠k)=g,那么一定存在无数组 { b i } \{b_i\} {bi}使 ∑ i = ̸ k b i x i = g ⋅ x k \sum_{i=\not k}b_ix_i=g \cdot x_k i≠kbixi=gxk,那么 a k a_k ak减去 g g g,在其他 x i x_i xi的系数 a i a_i ai上分别加上 b i b_i bi,最后 a k x k a_kx_k akxk的项可以到无限小,那么就可以使得其他项的系数 a i ∈ N ( i = ̸ k ) a_i\in N(i=\not k) aiN(i≠k)

(最后一步还是不严谨呢。。。要是有大佬会证请务必告诉我一声)

上代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 3000 + 10, inf = 1e9 + 7;
int n, m, g, minl, sell;
bool vis[N];
int dis[N];

queue<int> que;
bool inq[N];
void Spfa()
{
    memset(dis, 0x3f, sizeof(dis));
    que.push(0); dis[0] = 0; inq[0] = 1;
    while (!que.empty()){
        int u = que.front(); que.pop(); inq[u] = 0;
        for (int i = 1; i < N; ++ i)
            if (vis[i]){
         		int v = (u + i) % sell;
                if (dis[v] > dis[u] + i){
                    dis[v] = dis[u] + i;
                    if (!inq[v]){
                        inq[v] = 1;
                        que.push(v);
                    }
                }
            }
        }
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++ i){
        int x;
        scanf("%d", &x);
        for (int j = 0; j <= m && x - j > 0; ++ j)
            vis[x - j] = 1;
    }
    g = 0;
    for (int i = 1; i < N; ++ i)
        if (vis[i])
            g = __gcd(g, i);
    if (vis[1] || g > 1){cout << -1 << endl; return 0;}
    for (int i = 1; i < N; ++ i){
        if (!vis[i]) continue;
        if (!minl) minl = i;
        else if (minl) {sell = i; break;}
    }
    Spfa();
    minl = 0;
    for (int i = 0; i < sell; ++ i)
        minl = max(minl, dis[i] - sell);
    cout << minl << endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值