算法训练 Beaver's Calculator
时间限制:3.0s 内存限制:256.0MB
问题描述
从万能词典来的聪明的海狸已经使我们惊讶了一次。他开发了一种新的计算器,他将此命名为"Beaver's Calculator 1.0"。它非常特别,并且被计划使用在各种各样的科学问题中。
为了测试它,聪明的海狸邀请了n位科学家,编号从1到n。第i位科学家给这个计算器带来了 ki个计算题。第i个科学家带来的问题编号1到n,并且它们必须按照编号一个一个计算,因为对于每个问题的计算都必须依赖前一个问题的计算结果。
每个教授的每个问题都用一个数 ai, j 来描述,i(1≤i≤n)是科学家的编号,j(1≤j≤ ki )是问题的编号, ai, j 表示解决这个问题所需资源单位的数量。
这个计算器非常不凡。它一个接一个的解决问题。在一个问题解决后,并且在下一个问题被计算前,计算器分配或解放资源。
计算器中最昂贵的操作是解放资源,解放远远慢于分配。所以对计算器而言,每一个接下来的问题所需的资源不少于前一个,是非常重要的。
给你关于这些科学家所给问题的相关信息。你需要给这些问题安排一个顺序,使得“坏对”尽可能少。
所谓“坏对”,就是相邻两个问题中,后一个问题需求的资源比前一个问题少。别忘了,对于同一个科学家给出的问题,计算它们的相对顺序必须是固定的。
输入格式
第一行包含一个整数n,表示科学家的人数。接下来n行每行有5个整数,ki, ai, 1, xi, yi, mi (0 ≤ ai, 1 < mi ≤ 109, 1 ≤ xi, yi ≤ 109) ,分别表示第i个科学家的问题个数,第1个问题所需资源单位数,以及3个用来计算 ai, j 的参量。ai, j = (ai, j - 1 * xi + yi)mod mi。
输出格式
第一行输出一个整数,表示最优顺序下最少的“坏对”个数。
如果问题的总个数不超过200000,接下来输出 行,表示解决问题的最优顺序。每一行两个用空格隔开的整数,表示这个问题所需的资源单位数和提供这个问题的科学家的编号。
样例
输入
2
2 1 1 1 10
2 3 1 1 10
样例输出
0
1 1
2 1
3 2
4 2
数据规模和约定
20%的数据 n = 2, 1 ≤ ki ≤ 2000;
另外30%的数据 n = 2, 1 ≤ ki ≤ 200000;
剩下50%的数据 1 ≤ n ≤ 5000, 1 ≤ ki ≤ 5000。
思路:由于顺序不可逆,每个科学家的最小“坏对”是确定的,将所有科学家的“坏对”记为 aans ,aans的最大值即为 ans。 我们可以将“坏对”较少的科学家的问题 往 最多的科学家的问题 中插入即可。注意:1)long long 2)要中间逐层排序输出,不能在最后全部输出。
#include <queue>#include <functional>#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <stack>#include <vector>#include <set>#include <map>#include <string>#include <cmath>#include <cstdlib>#include <ctime>#include <assert.h>#define REP(i,k,n) for(int i=k;i<n;i++)#define REPP(i,k,n) for(int i=k;i<=n;i++)#define scan(d) scanf("%d",&d)#define scann(n,m) scanf("%d%d",&n,&m)#define mst(a,k) memset(a,k,sizeof(a));#define LL long long#define eps 1e-8#define INF 0x3f3f3f3f#define mod 1000000007using namespace std;#define N 5005#define M 200005LL k1[N],k2[N],a[N],x[N],y[N],m[N],num[N];struct node{ LL num,index; //num 代表问题消耗的资源量 , index 表示问题属于 第 index 个科学家 bool friend operator <(node a,node b){ return a.num > b.num; // 按消耗资源量从小到大排序 }}now,nex;priority_queue<node>que; // 用队列实现排序输出int main(){ int n; scan(n); int sum = 0; REPP(i,1,n){ scan(k1[i]); //k1[i] 第i个科学家的问题数 k2[i]第i个科学家已经处理的问题数 scann(a[i],x[i]); //a[i] 第i个科学家当前问题消耗的资源 scann(y[i],m[i]); sum+=k1[i]; //用 sum记录总问题数 } bool flag = 0; if(sum <= 200000) flag =1; //如果总问题数大于 200000 不用输出 int ans = 0; //记录 所有科学家的“坏对”中最大的那个 REPP(i,1,n){ int aans = 0; //科学家的坏对 LL kk,aa; kk=1;aa=a[i]; while(kk < k1[i]){ LL w = (aa * x[i] + y[i]) % m[i]; if(w < aa) aans++; //从大变小 即一个 “坏对” aa = w; kk++; } ans = max(aans,ans); } cout<<ans<<endl; //先输出“坏对”数 int tol = 0; //已经处理的问题数 while(tol<sum){ REPP(i,1,n){ if(k2[i] < k1[i]){ //第i个科学家的问题还未处理完 now.num = a[i]; now.index = i; if(flag) que.push(now); //如果要输出,则入队 tol++; k2[i]++; a[i] = (a[i] * x[i] + y[i]) % m[i]; while(a[i] >= now.num && k2[i] < k1[i]){ //每位科学家的问题一层一层进行处理,即当 a[i] < now.num,出现“坏对”停止 now.num = a[i]; if(flag) que.push(now); //如果要输出,则入队 tol++; k2[i]++; a[i] = (a[i] * x[i] + y[i]) % m[i]; //求出下一个要解决问题所需要的资源量 a[i] } } } if(flag){ while(!que.empty()){ //一层一层“坏对”进行处理,从而实现问题的插入 now = que.top(); que.pop(); cout<<now.num<<" "<<now.index<<endl; } } }}
问题描述
从万能词典来的聪明的海狸已经使我们惊讶了一次。他开发了一种新的计算器,他将此命名为"Beaver's Calculator 1.0"。它非常特别,并且被计划使用在各种各样的科学问题中。
为了测试它,聪明的海狸邀请了n位科学家,编号从1到n。第i位科学家给这个计算器带来了 ki个计算题。第i个科学家带来的问题编号1到n,并且它们必须按照编号一个一个计算,因为对于每个问题的计算都必须依赖前一个问题的计算结果。
每个教授的每个问题都用一个数 ai, j 来描述,i(1≤i≤n)是科学家的编号,j(1≤j≤ ki )是问题的编号, ai, j 表示解决这个问题所需资源单位的数量。
这个计算器非常不凡。它一个接一个的解决问题。在一个问题解决后,并且在下一个问题被计算前,计算器分配或解放资源。
计算器中最昂贵的操作是解放资源,解放远远慢于分配。所以对计算器而言,每一个接下来的问题所需的资源不少于前一个,是非常重要的。
给你关于这些科学家所给问题的相关信息。你需要给这些问题安排一个顺序,使得“坏对”尽可能少。
所谓“坏对”,就是相邻两个问题中,后一个问题需求的资源比前一个问题少。别忘了,对于同一个科学家给出的问题,计算它们的相对顺序必须是固定的。
输入格式
第一行包含一个整数n,表示科学家的人数。接下来n行每行有5个整数,ki, ai, 1, xi, yi, mi (0 ≤ ai, 1 < mi ≤ 109, 1 ≤ xi, yi ≤ 109) ,分别表示第i个科学家的问题个数,第1个问题所需资源单位数,以及3个用来计算 ai, j 的参量。ai, j = (ai, j - 1 * xi + yi)mod mi。
输出格式
第一行输出一个整数,表示最优顺序下最少的“坏对”个数。
如果问题的总个数不超过200000,接下来输出 行,表示解决问题的最优顺序。每一行两个用空格隔开的整数,表示这个问题所需的资源单位数和提供这个问题的科学家的编号。
样例
输入
2
2 1 1 1 10
2 3 1 1 10
样例输出
0
1 1
2 1
3 2
4 2
数据规模和约定
20%的数据 n = 2, 1 ≤ ki ≤ 2000;
另外30%的数据 n = 2, 1 ≤ ki ≤ 200000;
剩下50%的数据 1 ≤ n ≤ 5000, 1 ≤ ki ≤ 5000。
思路:由于顺序不可逆,每个科学家的最小“坏对”是确定的,将所有科学家的“坏对”记为 aans ,aans的最大值即为 ans。 我们可以将“坏对”较少的科学家的问题 往 最多的科学家的问题 中插入即可。注意:1)long long 2)要中间逐层排序输出,不能在最后全部输出。
#include <queue>#include <functional>#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <stack>#include <vector>#include <set>#include <map>#include <string>#include <cmath>#include <cstdlib>#include <ctime>#include <assert.h>#define REP(i,k,n) for(int i=k;i<n;i++)#define REPP(i,k,n) for(int i=k;i<=n;i++)#define scan(d) scanf("%d",&d)#define scann(n,m) scanf("%d%d",&n,&m)#define mst(a,k) memset(a,k,sizeof(a));#define LL long long#define eps 1e-8#define INF 0x3f3f3f3f#define mod 1000000007using namespace std;#define N 5005#define M 200005LL k1[N],k2[N],a[N],x[N],y[N],m[N],num[N];struct node{ LL num,index; //num 代表问题消耗的资源量 , index 表示问题属于 第 index 个科学家 bool friend operator <(node a,node b){ return a.num > b.num; // 按消耗资源量从小到大排序 }}now,nex;priority_queue<node>que; // 用队列实现排序输出int main(){ int n; scan(n); int sum = 0; REPP(i,1,n){ scan(k1[i]); //k1[i] 第i个科学家的问题数 k2[i]第i个科学家已经处理的问题数 scann(a[i],x[i]); //a[i] 第i个科学家当前问题消耗的资源 scann(y[i],m[i]); sum+=k1[i]; //用 sum记录总问题数 } bool flag = 0; if(sum <= 200000) flag =1; //如果总问题数大于 200000 不用输出 int ans = 0; //记录 所有科学家的“坏对”中最大的那个 REPP(i,1,n){ int aans = 0; //科学家的坏对 LL kk,aa; kk=1;aa=a[i]; while(kk < k1[i]){ LL w = (aa * x[i] + y[i]) % m[i]; if(w < aa) aans++; //从大变小 即一个 “坏对” aa = w; kk++; } ans = max(aans,ans); } cout<<ans<<endl; //先输出“坏对”数 int tol = 0; //已经处理的问题数 while(tol<sum){ REPP(i,1,n){ if(k2[i] < k1[i]){ //第i个科学家的问题还未处理完 now.num = a[i]; now.index = i; if(flag) que.push(now); //如果要输出,则入队 tol++; k2[i]++; a[i] = (a[i] * x[i] + y[i]) % m[i]; while(a[i] >= now.num && k2[i] < k1[i]){ //每位科学家的问题一层一层进行处理,即当 a[i] < now.num,出现“坏对”停止 now.num = a[i]; if(flag) que.push(now); //如果要输出,则入队 tol++; k2[i]++; a[i] = (a[i] * x[i] + y[i]) % m[i]; //求出下一个要解决问题所需要的资源量 a[i] } } } if(flag){ while(!que.empty()){ //一层一层“坏对”进行处理,从而实现问题的插入 now = que.top(); que.pop(); cout<<now.num<<" "<<now.index<<endl; } } }}