• [国家集训队]墨墨的等式(同余最短路)


    前置题目双倍经验):[洛谷P3403]跳楼机

    题目

    ([l,r])中有多少个b可以使得方程(sum{a_ix_i = b})有非负整数解((nleq 12,l,rleq 10^{12}))

    思路

    问题等价于求(solve(r)-solve(l-1))

    考虑怎么求(solve(r)),设(f_i)表示(x_1=0)时(只用其他的数)能得到的最小的模(a_1)(i)的数
    显然,(f_i)可以通过加(a_{2,3,...,n})变成转移给(f_{(i+a_{2,3,4....n})\%a_1}),将这个关系建边,跑一遍最短路即可得到(f)数组
    由于(f)已经是不用(a_1)情况下可以得到的最小的值了,所以给(f)只加(a_1)就可以得到所有的合法情况;而根据(f)的定义,只在(f_i)上加(a_1)不会使得不同(i)出现(f_i+a_1x_1)相等的情况,所以分别处理每个(f_i)再加起来就行了

    (ans=sum{((r-f_i)/a_1 +1)})

    P.S.注意特判存在(a_i=1)的情况;可以看出边数与(a_1)有关,取最小的(a)作为(a_1)(当然不取应该也没什么事)

    Code

    #include<bits/stdc++.h>
    #define N 500005
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define Max(x,y) ((x)>(y)?(x):(y))
    using namespace std;
    typedef long long ll;
    int n,a[15];
    ll l,r,dis[N];
    bool ccf,vis[N];
    
    struct Edge
    {
    	int next,to,dis;
    }edge[N*12];int head[N],cnt;
    void add_edge(int from,int to,int dis)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	edge[cnt].dis=dis;
    	head[from]=cnt;
    }
    
    template <class T>
    void read(T &x)
    {
    	char c; int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
    }
    void dijkstra()
    {
    	priority_queue< pair<ll,int> > q;
    	memset(dis,100,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[0]=0;
    	q.push(make_pair(-dis[0],0));
    	while(!q.empty())
    	{
    		int u=q.top().second; q.pop();
    		if(vis[u]) continue;
    		vis[u]=1;
    		for(int i=head[u];i;i=edge[i].next)
    		{
    			int v=edge[i].to;
    			if(dis[v]>dis[u]+edge[i].dis)
    			{
    				dis[v]=dis[u]+edge[i].dis;
    				if(!vis[v]) q.push(make_pair(-dis[v],v));
    			}
    		}
    	}
    }
    int main()
    {
    	read(n);read(l);read(r); --l;
    	for(int i=1;i<=n;++i) read(a[i]),ccf|=(a[i]==1);
    	if(ccf) { cout<<r-l<<endl; return 0; }
    	sort(a+1,a+n+1);
    	for(int i=0;i<a[1];++i)
    	  for(int j=2;j<=n;++j)
    	    add_edge(i,(i+a[j])%a[1],a[j]);
    	dijkstra();
    	ll ans=0;
    	for(int i=0;i<a[1];++i)
    	{
    		if(dis[i]<=r) ans+=(r-dis[i])/a[1]+1;
    		if(dis[i]<=l) ans-=(l-dis[i])/a[1]+1;
    	}
    	cout<<ans<<endl;
    	return 0;	
    }
    
  • 相关阅读:
    2020年秋第四五周-代码规范,结对要求
    2020年秋第四五周-四则运算试题生成
    同时装了WPS和Office新建的时候不知道是哪个文件
    开讲啦郑强演讲:你为什么读大学?
    PC版kindle无法连接网络
    前端编程良好习惯
    教你隐藏盘符,把你的小姐姐藏起来
    腾讯,比你想的更有趣
    U盘之父中国朗科的一生:我曾打败索尼,如今却只能靠收租为生
    动作之概述
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11772502.html
Copyright © 2020-2023  润新知