• 【洛谷P2371】墨墨的等式【最短路】


    题目大意:

    题目链接:https://www.luogu.org/problem/P2371
    墨墨突然对等式很感兴趣,他正在研究 a1x1+a2x2+...+anxn=Ba_1x_1+a_2x_2+...+a_nx_n=B 存在非负整数解的条件,他要求你编写一个程序,给定nnan{an}、以及BB的取值范围,求出有多少BB可以使等式存在非负整数解。


    思路:

    在这里插入图片描述
    以下内容大部分摘自这篇题解
    B106Bleq 10^6的部分分就是一个裸的背包。但是这道题的范围是B1012Bleq 10^{12}
    若满足a1x1+a2x2+...+anxn=pa_1x_1+a_2x_2+...+a_nx_n=p,那么一定满足a1x1+a2x2+...+anxn=p+k×minna_1x_1+a_2x_2+...+a_nx_n=p+k imes minn。显然在pp越小时,kk能取到的值越大。
    minn=min{ai}minn=min{a_i}dis[i]dis[i]表示Bmod  minn=iBmod minn=ipp的最小值。
    对于每一个数字aia_i,建边j(j+ai)mod  minnj o (j+a_i)mod minn,其中j[0,minn)jin[0,minn)
    然后从0开始跑最短路,这样就可以求出dis[]dis[]了。
    那么B[0,k]Bin [0,k]时原式有非负整数解的数量即为i=0minn1(kdis[i]minn+1)sum^{minn-1}_{i=0}(frac{k-dis[i]}{minn}+1)


    代码:

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define mp make_pair
    using namespace std;
    typedef long long ll;
    
    const int N=15,M=500010;
    int n,tot,minn,a[N],head[M];
    ll Bmin,Bmax,dis[M];
    bool vis[M];
    
    struct edge
    {
    	int next,to,dis;
    }e[N*M];
    
    void add(int from,int to,int dis)
    {
    	e[++tot].to=to;
    	e[tot].dis=dis;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    void dij()
    {
    	priority_queue<pair<ll,int> > q;
    	q.push(mp(0,0));
    	memset(dis,0x3f3f3f3f,sizeof(dis));
    	dis[0]=0;
    	while (q.size())
    	{
    		int u=q.top().second;
    		q.pop();
    		if (vis[u]) continue;
    		vis[u]=1;
    		for(int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (dis[v]>dis[u]+(ll)e[i].dis)
    			{
    				dis[v]=dis[u]+(ll)e[i].dis;
    				q.push(mp(-dis[v],v));
    			}
    		}
    	}
    }
    
    ll count(ll k)
    {
    	ll ans=0;
    	for (ll i=0;i<minn;i++)
    		if (dis[i]<=k) ans+=(k-dis[i])/(ll)minn+1;
    	return ans;
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%lld%lld",&n,&Bmin,&Bmax);
    	minn=M;
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		minn=min(minn,a[i]);
    	}
    	for (int i=0;i<minn;i++)
    		for (int j=1;j<=n;j++)
    			add(i,(i+a[j])%minn,a[j]);
    	dij();
    	printf("%lld",count(Bmax)-count(Bmin-1));
    	return 0;
    }
    
  • 相关阅读:
    关于数论的一些总结
    gym101431B
    4.29训练题解
    hdu4347
    5.13训练的一些题解
    5.20训练的一些题解
    hdu4796
    hdu5984
    bzoj1941 hdu5992
    hdu4307
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998004.html
Copyright © 2020-2023  润新知