ACM一类方程问题的求解[最短路建模] bzoj2118

在ACM生涯里已经预见两回判断这种方程是否有解、有几个解的问题了。

例如:

1

给定非负整数a,b,c,n,请判断ax+by+cz=n是否存在(x,y,z)均为非负整数的解

题目链接:http://oj.xjtuacm.com/contest/14/problem/124/

再例如:

2

现有方程A1 * X1 + A2 * X2 + ... + An * Xn = P

A1, A2, ... , An为变量X1, X2, ... , Xn的系数

给定P的取值范围,求有多少个P使得方程存在非负整数解

题目链接:http://oj.xjtuacm.com/contest/14/problem/124/


而这种方程看似是数论问题往往能通过建模的方法转换成为图论里面最短路问题,很奇妙,对吧!

我们就拿题目2来说,设所有的给出的并且满足这个方程的P值,如果我们把P值对Ai进行取模,得到的数必然存在于Ai的剩余系中,也就是说

P%Ai 在区间[0,Ai-1]里面,乍一看,我们这样做岂不是把信息减损了?P映射到[0,Ai-1]相当于把模数相同的P都给去冲了。不用担心,我们有神奇的办法可以从剩余系[0,Ai-1]

中把所有的P无损的还原回来。

那么怎么做呢?

假设我们已经有了所有满足

(A1 * X1 + A2 * X2 + ... + An * Xn)%Ai = P%(Ai) = y

的y值,那么我们将y不断地增加Ai,然后判断得到的值是否在[Pmin,Pmax],是不是就可以得到所有的P值了呢。(哇,真的很奇妙!)

举个例子来说,

我们选取Ai等于5 

Pmin = 0,Pmax = 11,其中一个y = 2

那么2,2+5 = 7就是满足条件的所有P(%Ai = 2意义下)

但是这里是有一个坑点的,细心的朋友可以发现,我上面举得那个例子实际上是错误的,因为这样扩展y的方法会造成P的数量比真实情况下要多。

这样理解:

假设满足mod Ai = 2意义下的P只有7的话,那么根据上面的方法将会的得到一个假的P(P = 2),相当于无形之间把答案的数量增多了。

这就要求我们,保存一个最小的P(mod Ai = 2),在最小的这个P的基础上开始扩展(而不是从2的基础上开始扩展),就可以保证答案的正确性了。

由于这个Ai是可以随便取得,但是取最小的那个是最好的,因为取最小的那个Ai,剩余系是最小的,我们记A = min{Ai}

这样我们的问题就转化成为求所有对应A的剩余系中值的最小P,求出来这个以后直接进行扩展就好了。

而要求这样的P,我们就可以采用最短路的做法,具体怎么做呢?

我们把点定义成为剩余系中的值,把边定义成为,一个剩余系中的值向另一个剩余系中的值转移所需要的代价。

很明显的,d[0] = 0

然后从i出发,向(i+Ak)%A这个点进行转移的边的代价为Ai

也就是说我们可以把i和(i+Ak)做一条边权位Ak得边,

图建好以后,做一个dijkstra就好了

得到的d[i]就是对应剩余系中的i的P的最小值。


第一题和第二题是基本相同的,这里就不赘述了(值得一提的是,第一题还可以用扩展欧几里得来做)

代码:

第一题(最短路建模的方法)

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a,b,c;
long long n;
const int MAXN = 3e5; 
const int V_MAXN = 2e5; 
const long long INF = 1e18;  
int N,X,Y,MAX;  
int head[V_MAXN];  
struct edge{  int v;  int next;  int cost;  
}Es[MAXN<<1];  
long long d[MAXN];  
int cnt;  
typedef pair<int,int> P;  void dijkstra(int x){  for(int i = 0;i < MAXN;i++)  d[i] = INF;  d[x] = 0;  priority_queue<P,vector<P>,greater<P> > que;  que.push(P(0,x));  while(!que.empty()){  P p = que.top();que.pop();  int dis = p.first;  int v = p.second;  if(d[v] < dis) continue;  //for(int i = 0;i < 2*N+2;i++){  for(int e = head[v];e!= -1;e = Es[e].next){  int cost = Es[e].cost;  int i = Es[e].v;  if(cost + d[v] < d[i]){  d[i] = d[v] + cost;  que.push(P(d[i],i));  }  }  }  
}  
inline void add_edge(int i,int j,int cost){  //G[i][j] = cost;  Es[cnt].v = j;  Es[cnt].cost = cost;  Es[cnt].next = head[i];  head[i] = cnt++;  
}  
void init(){  cnt = 0;  memset(head,-1,sizeof(head));  
}  
int main(){int cas = 0;while(~scanf("%d%d%d%lld",&a,&b,&c,&n)){init();int p = min(a,b);p = min(p,c);for(int i = 0;i < p;i++){add_edge(i,(i+a)%p,a);add_edge(i,(i+b)%p,b);add_edge(i,(i+c)%p,c);}dijkstra(0);for(int i = 0;i < p;i++){if(d[i] <= n){if((n - d[i])%p == 0){printf("Case #%d: Yes\n",++cas);goto ns;}}}printf("Case #%d: No\n",++cas);ns:;} return 0;
}


第一题(扩展欧几里得地方法)

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long int LL;
LL extgcd(LL a,LL b,LL& x,LL& y)
{LL d = a;if(b != 0){d = extgcd(b,a%b,y,x);y -= (a/b)*x;}else{x = 1;y = 0;}return d;
}
void solve(){LL k[3],n;int cas = 0;while(~scanf("%lld%lld%lld%lld",&k[0],&k[1],&k[2],&n)){sort(k,k+3);LL limit = min(n/k[2],k[0]);int f = 0;if(n%k[2] != 0)for(int i = 0;i <= limit;i++){LL x,y;LL res = n - (LL)i*k[2];LL d = extgcd(k[0],k[1],x,y);if(res % d != 0){continue;}else{if(x >=0 && y >= 0){f = 1;break;}else {if(y < 0){swap(x,y);swap(k[0],k[1]);} LL r = (res%k[1])*(-x)%k[1];if(r == 0){f = 1;break;}else{//cout<<double(n)<<endl;if(res >= 1000000){if(res/k[0]*y >= res/k[1]*(-x)){f = 1;break;}}elseif(res*y/k[0] >= res*(-x)/k[1]+1){f = 1;break;}}}}}else{f = 1;}printf("Case #%d: ",++cas);if(f){puts("Yes");}else{puts("No");}}
}
int main(){solve();return 0;
}


第二题(最短路建模的方法)

#include <iostream>  
#include <cstdio>  
#include <vector>  
#include <algorithm>  
#include <queue>  
#include <cstring>  
using namespace std;  
#define int long long   
const int MAXN = 1e7; 
const int V_MAXN = 1e7; 
const int INF = 1e18;  
int N,X,Y,MAX;  
int head[V_MAXN];  
struct edge{  int v;  int next;  int cost;  
}Es[MAXN<<1];  
int d[MAXN];  
int cnt;  
typedef pair<int,int> P;  void dijkstra(int x){  for(int i = 0;i < MAXN;i++)  d[i] = INF;  d[x] = 0;  priority_queue<P,vector<P>,greater<P> > que;  que.push(P(0,x));  while(!que.empty()){  P p = que.top();que.pop();  int dis = p.first;  int v = p.second;  if(d[v] < dis) continue;  //for(int i = 0;i < 2*N+2;i++){  for(int e = head[v];e!= -1;e = Es[e].next){  int cost = Es[e].cost;  int i = Es[e].v;  if(cost + d[v] < d[i]){  d[i] = d[v] + cost;  que.push(P(d[i],i));  }  }  }  
}  
inline void add_edge(int i,int j,int cost){  //G[i][j] = cost;  Es[cnt].v = j;  Es[cnt].cost = cost;  Es[cnt].next = head[i];  head[i] = cnt++;  
}  
void init(){  cnt = 0;  memset(head,-1,sizeof(head));  
}  
int n;
long long Pmin,Pmax;
int A[20];
main(){while(scanf("%lld%lld%lld",&n,&Pmin,&Pmax) != EOF){init();int p = INF;for(int i = 1;i <= n;i++){scanf("%lld",&A[i]);if(A[i])p = min(p,A[i]);}if(p >= INF) {puts(Pmin == 0?"1":"0");continue;}for(int i = 0 ;i < p;i++){for(int j = 1;j <= n;j++){add_edge(i,(i + A[j])%p,A[j]);}}dijkstra(0);long long ans = 0;for(int i = 0;i < p;i++){if(d[i] < INF){long long l,r; if(d[i] < Pmin) {l = (Pmin - 1 - d[i])/p + 1;}else{l = 0;}if(d[i] <= Pmax){r = (Pmax - d[i])/p + 1; } else{r = 0;}ans += r - l;}}printf("%lld\n",ans);}return 0;
}





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3、数据库中的字符集和校对集

字符集 字符集指的就是存储数据到硬盘时用到的编码方式&#xff0c;mysql中操作字符集的基本sql如下&#xff1a; show character set; -- 查看服务器支持哪些字符集 show variables like ‘character_set%’; -- 查看服务器对外处理的默认字符集 set character_set_clien…

P3810-[模板]三维偏序(陌上花开)【CDQ分治,树状数组】

正题 题目链接:https://www.luogu.com.cn/problem/P3810 题目大意 nnn个三元组(a,b,c)(a,b,c)(a,b,c)&#xff0c;f(i)∑i1n,j≠i[aj≤ai&bj≤bi&bj≤bi]f(i)\sum_{i1}^{n,j\neq i}[a_j\leq a_i\&b_j\leq b_i\&b_j\leq b_i]f(i)i1∑n,j​i​[aj​≤ai​&…

4、mysql数据库的权限管理

权限管理指的是对试图连接和操作数据库服务器的用户进行访问控制 关于权限管理的一些sql实例如下&#xff1a; -- 首先通过cmd窗口连接mysql服务器 mysql -u root -p *******-- mysql数据库下有一个user表&#xff0c;里边存储的是user的信息 select user from user;-- 添加一…

P3157-[CQOI2011]动态逆序对【CDQ分治,树状数组】

正题 题目链接:https://www.luogu.com.cn/problem/P3157 题目大意 一个长度为nnn序列&#xff0c;每次删除一个数&#xff0c;求删除前的逆序对数量。 解题思路 时光倒流之后&#xff0c;我们变为每次加入一个数求逆序对数量。 我们将加入一个数的贡献分为后面和前面两部分…

5、mysql中的库操作

对数据库的操作包括对数据库的增、删、改、查 接下来介绍一下基本的sql语句 -- 创建数据库 create database 数据库名称 字符集&#xff08;charset utf8&#xff09;; create database czpdb charset urf8; -- czpdb 数据库在磁盘中就是一个文件夹&#xff0c;它下边的db.op…

P5887-Ringed Genesis【GCD】

正题 题目链接:https://www.luogu.com.cn/problem/P5887?contestId24709 题目大意 nnn个洞围成一个环&#xff0c;兔子每次会往前跳kkk个洞&#xff0c;mmm只兔子给出其实位置&#xff0c;求有多少个洞不会被经过。 解题思路 我们可以发现kxnyckxnyckxnyc&#xff0c;有c∣g…

Network of Schools POJ - 1236 tarjan强连通分量缩点

A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of…

6、mysql中字段

对数据表的操作是比较重要的&#xff0c;在实际开发中&#xff0c;日常做的主要工作就是对数据表的操作 对数据表的操作分为两大部分&#xff1a;操作数据表的结构、操作数据表中的数据 组成数据表的基本单元就是字段&#xff0c;所以&#xff0c;接下来先介绍一下mysql中的字…

bzoj3732-Network【Kruskal重构树模板】

正题 题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id3732 题目大意 一张图&#xff0c;每次询问两个点&#xff0c;求这两个点之间路径的最大值的最小是多少。 解题思路 构造一颗KruskalKruskalKruskal重构树然后就是模板了。 codecodecode #include<cstd…

在Linux环境下使用Apache部署ASP.NET Core

在前几篇文章中我们一起探讨了如何在Linux环境中安装ASP.NET Core运行时环境及将ASP.NET Core项目部署在Jexus中&#xff0c;这篇文章中我们将探讨如何将ASP.NET Core部署于Apache&#xff08;阿帕奇&#xff09;中。 很幸运能够和大家一起学习和探讨ASP.NET Core本文章运行…

7、mysql中的表结构操作

之前已经介绍了字段以及修饰字段的一些属性&#xff0c;根据特定的对象可以将字段进行组合&#xff0c;这就有了表的概念&#xff0c;接下来介绍一下如何操 作数据表结构。对数据表结构的操作有添加表、修改表、删除表、查看表结构。 添加表 格式&#xff1a;create table […

P3698-[CQOI2017]小Q的棋盘【树形dp】

正题 题目链接:https://www.luogu.com.cn/problem/P3698 题目大意 nnn个点的树&#xff0c;求从111出发走kkk步最多能到多少个节点。(重复走不算) 解题思路 做法与树形背包类似&#xff0c;但是需要注意的是最后不需要返回原点。 fi,j,0/1f_{i,j,0/1}fi,j,0/1​表示第iii个点…

傲娇码农的自我修养

一个热爱自己职业的人一定会对自己的工作充满自豪感&#xff0c;同样&#xff0c;也应该对自己的工作充满热情和自信。对自己的专业能力骄傲而不自满。身为一个码农&#xff0c;如果你热爱自己的工作&#xff0c;我想&#xff0c;你很有可能也是一位傲娇码农。在我的眼里&#…

8、mysql数据表中数据的增删改

已经介绍了数据表的创建、修改和删除&#xff0c;接下来介绍一下如何往数据表中添加数据、修改数据、删除数据 添加数据 # 默认给表中所有字段添加数据&#xff0c;要保证添加的数据列和字段列一致 insert into user values(5,12,wangwu);# 为表中指定的字段添加数据 insert …

P4071-[SDOI2016]排列计数【组合计数,错排】

正题 题目链接:https://www.luogu.com.cn/problem/P4071 题目大意 每次询问n,mn,mn,m。求有多少个nnn的排列使得aiia_iiai​i的数量恰好为mmm个。 解题思路 首先nnn个之中选择mmm个aiia_iiai​i&#xff0c;选择的方案数为CnmC_{n}^mCnm​。剩下的错排就好了 错排推导di(i−…

中国到底有多少个.NET 程序员?都在哪个城市写代码?

中国到底多少个.NET 程序员&#xff0c;对于这个问题&#xff0c;似乎没有一个准确的答案&#xff0c;而且最近很多使用.NET 开发技术的老板在抱怨找不到.NET 开发人员&#xff0c;所以我想基于我的公众号粉丝数据给大家分享下中国的.NET程序员到底有多少&#xff0c;他们也都是…

9、mysql数据表中数据的查询(1)

使用数据库最终要的莫过于查询数据&#xff0c;所以这里将会详细的介绍mysql数据库中查询语句的使用 普通查询 使用基本的select关键字进行查询&#xff0c;其语法及使用如下 # 格式 select [select选项] 字段 [as 别名] from 数据表;# 查询出所有字段的值&#xff0c;不建议…

P4092-[HEOI2016/TJOI2016]树【线段树,倍增】

正题 题目链接:https://www.luogu.com.cn/problem/P4092 题目大意 nnn个点的树要求支持以下操作 给一个点打上标记求一个节点最近的打上了标记的祖先 解题思路 就是求深度最大的打上了标记的祖先&#xff0c;先跑一遍dfsdfsdfs序&#xff0c;然后对于修改操作就在线段树上整…

微软西雅图总部DevOps交流总结

本文转自Study4台湾社区。Study4台湾社区&#xff0c;成立于2011/9/25&#xff0c;希望藉由社群推广的力量&#xff0c;让台下的朋友听到来自不同县市的大师讲课&#xff0c;也让台上年轻一辈的技术传教士能不断的琢磨并且追上大师这是一个社群&#xff0c;社区希望透过分享&am…

12、数据库的设计范式

设计的数据表一般都是根据实际业务实体模型进行创建&#xff0c;为了提高存储的效率&#xff0c;设计数据表时一般按着几个标准进行设计 第一范式每个字段的数据具有原子性&#xff0c;使用字段的数据时不用再解析&#xff0c;可直接使用第二范式表的每一个字段都必须依赖主键…