luogu P2662 牛场围栏

传送门

因为一个木板可以切掉最多\(m\),所以可以先预处理哪些长度的木板可用,开个桶,然后对\([l-m,l]\)打标记,再把打了标记的数取出来

假设可用长度\(a_1,a_2,,,a_n\)从小到大排好了序,我们可以先不用\(a_1\),只用后面的长度拼,然后用\(a_1\)凑,所以设\(di_i\)为能用后面的凑出的并且\(mod\ a_1\)\(i\)的最短长度(\(di_0=0\)),然后可以把\(a_2,,,a_n\)当做从位置\(i\)连向\((i+a_j)mod\ a_1\),边权为\(a_j\)的边,跑个最短路就可以求出所有\(di\)

最后答案为\(\max_{i=0}^{a_1-1}di_i-a_1\),因为这些长度\(d_i\)没有用\(a_1\)拼出来,这里面任何一个长度减\(a_1\)就是不能拼出的长度

注意判无解

#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register

using namespace std;
const int N=3000+10,M=N*N;
il LL rd()
{
    re LL x=0,w=1;re char ch;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[M],nt[M],w[M],hd[N],tot=1;
il void add(int x,int y,int z) {++tot,to[tot]=y,nt[tot]=hd[x],w[tot]=z,hd[x]=tot;}
struct node
{
  int x,d;
  bool operator < (const node &b) const {return d>b.d;}
};
int n,m,a[N],di[N];
short vv[N];
il int gcd(int a,int b){return b?gcd(b,a%b):a;}

int main()
{
  n=rd(),m=rd();
  while(n--)
    {
      int x=rd();
      ++vv[max(1,x-m)],--vv[x+1];
    }
  n=0;
  for(int i=1;i<=N-10;i++)
    {
      vv[i]+=vv[i-1];
      if(vv[i]>0) a[++n]=i;
    }
  int gg=a[1];
  for(int i=2;i<=n;i++) gg=gcd(gg,a[i]);
  if(gg>1||a[1]==1) {puts("-1");return 0;}  //判无解
  for(int i=0;i<a[1];i++)
    for(int j=2;j<=n;j++)
      add(i,(i+a[j])%a[1],a[j]);
  memset(di,63,sizeof(di));
  di[0]=0;
  priority_queue<node> q;
  q.push((node){0,0});
  while(!q.empty())
    {
      int x=q.top().x,d=q.top().d;
      q.pop();
      if(d>di[x]) continue;
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(di[y]>di[x]+w[i])
            {
              di[y]=di[x]+w[i];
              q.push((node){y,di[y]});
            }
        }
    }
  int ans=a[1];
  for(int i=0;i<a[1];i++) ans=max(ans,di[i]);
  printf("%d\n",ans-a[1]);
  return 0;
}

posted @ 2018-10-03 11:10  ✡smy✡  阅读(131)  评论(0编辑  收藏  举报