1.原题目
问题描述
从万能词典来的聪明的海狸已经使我们惊讶了一次。他开发了一种新的计算器,他将此命名为"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。
2.题目分析
感觉整个人是懵逼的,一开始妄想用优先队列实现写着写着觉得不对劲了,然后发现肯定是超时只是拿到了20分…
然后讲一下这个题目的正解:归并排序加上贪心
我们首先列举一下题目中关键的信息点
1.科学家问题它们必须按照编号一个一个计算,因为对于每个问题的计算都必须依赖前一个问题的计算结果
2.如果问题的总个数不超过200000,接下来输出 sum(k)行
一开始想暴力求一下总数是多少,但是真正的做法如下:首先我们知道对于一个科学家的问题解决的顺序是不能改变的,所以每一个科学家带来的任务的坏点对数是固定的 那么结论应该是什么?结果总数就是 一位科学家任务坏点数的最大值!
我们明确一下,因为同一科学家任务顺序不变,插入或者被插入不改变其坏点对数,那么坏点对数至少是一位科学家任务坏点对数最大值那么大。 坏点对说白了就是A[i]比A[i+1]大,那么我们将科学家任务可以分为K个升序段。那么其他科学家段数比最大那位科学家任务分段数少,那么!其他科学家的任务的升序段一定都可以依次插入!这就很像归并排序的原理了!然后我们记录一下最后顺序输出就行了!
写了一下午加上一晚上,太菜了…下面是我的丑代码:
#include<bits/stdc++.h>
using namespace std;
struct node{
int pos,count; //pos表示当前位置,count表示该科学家有的问题总数
long long num[5001]; //求出每位科学家的问题值
}p[5001];
struct ms{ //答案
long long number,id;
}ans[200001],temp[200001]; //temp数组为了sort中间替换
int n,k,total=0,start,end;
long long length=0;
void merge(int x,int y){
int mid=(x+y)>>1,i=x,j=mid+1,kkk=x;
while(i!=mid+1&&j!=y+1){
if(ans[i].number<=ans[j].number){
temp[kkk].id=ans[i].id; //一定是ans结果不变,然后赋值到temp值中,再将结果整体赋值
temp[kkk++].number=ans[i++].number;
}
else {
temp[kkk].id=ans[j].id;
temp[kkk++].number=ans[j++].number;
}
}
while(i<=mid){
temp[kkk].id=ans[i].id;
temp[kkk++].number=ans[i++].number;
}
while(j<=y){
temp[kkk].id=ans[j].id;
temp[kkk++].number=ans[j++].number;
}
for(i=x;i<=y;i++){ //将结果整体赋值
ans[i]=temp[i];
}
}
void mergesort(int x,int y){
if(x<y){
int mid=(x+y)>>1;
mergesort(x,mid);
mergesort(mid+1,y);
merge(x,y);
}
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++){
long long t1,t2,t3,t4,times=0;
scanf("%d%lld%lld%lld%lld",&k,&t1,&t2,&t3,&t4); //注意第一个是%d啊!输入不正确可不行!(不是%lld)
p[i].pos=1;
p[i].count=k;
p[i].num[1]=t1;
length+=k; //统计总长
for(int j=2;j<=k;j++){
p[i].num[j]=(p[i].num[j-1]*t2+t3)%t4;
if(p[i].num[j]<p[i].num[j-1]){
times++;
}
}
if(times>total){ //更新一位科学家任务坏点对数
total=times;
}
}
cout<<total<<endl;
if(length<=200000){ //注意这是一个条件!
start=end=1;
while(end<=length){
for(int i=1;i<=n;i++){ //依次写入升序段
int position=p[i].count+1;
for(int j=p[i].pos;j<=p[i].count;j++){
if(j!=p[i].pos&&p[i].num[j]<p[i].num[j-1]){
position=j;
break;
}
ans[end].id=i;
ans[end++].number=p[i].num[j];
}
p[i].pos=position; //重新设置起点
}
mergesort(start,end-1);
start=end; //更新位置
}
for(int i=1;i<=length;i++){
printf("%lld %lld\n", ans[i].number, ans[i].id);
}
}
return 0;
}