Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1140 Accepted Submission(s): 397
2 1 10 1 2 10 1 4 5 20 7 6 14 3 5 9 1 7 21 12
1 1 8 1
解题思路:
本题的大致意思是共有2的32次方个学生,编号1到2的32次方,有N个社团(0<N<=20000)每个社团对应着三个数值,ai,bi,ci,学生数目众多,每个社团不能每个学生都发到传单,所以按照一定规律(等差数列)来发给特定编号的学生,每个社团所发的学生编号为 ai, ai+1*ci, ai+2*ci, ai+3*ci, ai+4*ci,........直到(ai+k*ci<=bi, ai+(k+1)*ci>bi)为止,最多有一个学生他所得到的传单总数为奇数,要求输出这个学生的编号和他所得的传单数。一开始的思路是开 2的32次方大的数组来保存每个学生发到的传单数,然后遍历一遍,遇到奇数则输出编号和值得大小,但是数组太大了,开不出来,所以不能以学生的数量开数组,而是以社团的数量考虑,下面代码中是把每个社团都设为结构体,保存着ai,bi,ci。编写cal(int x)函数,用来求前x个学生所得到的传单总数,因为奇数+偶数=奇数,偶数+偶数一定等于偶数,所以如果算出cal(int x)为奇数,那么那个得到传单数为奇数的同学一定在前x个学生里面,但不一定编号就是x,采用二分的方法,将这个传单数位奇数的同学的编号精确到1。
代码:
#include <iostream> #include <algorithm> using namespace std; int N; struct tuan { long long ai; long long bi; long long ci; }shetuan[20005];//社团结构体 long long cal(long long mid) { long long sum=0; for(int i=1;i<=N;i++) { long long limit=min(shetuan[i].bi,mid);//bi是限制范围,最大编号不能超过它 if(limit>=shetuan[i].ai) { sum+=(limit-shetuan[i].ai)/shetuan[i].ci+1;//计算前Mid个学生得到的第i个社团的传单总数 } }//计算前mid个学生得到的所有社团的传单的总数 return sum; } int main() { int i; while(cin>>N) { for(i=1;i<=N;++i) cin>>shetuan[i].ai>>shetuan[i].bi>>shetuan[i].ci; long long l=1,r=1LL<<31;//注意这里要写为1LL<<31 不能只写为1<<31 while(l<r) { long long mid=(l+r)/2; if(cal(mid)%2==1) r=mid; else l=mid+1; }//二分,把得到传单为奇数的学生的编号精确到1,即l if(l==1LL<<31) cout<<"DC Qiang is unhappy."<<endl;//没有得到传单数为奇数的学生 else cout<<l<<" "<<cal(l)-cal(l-1)<<endl;//前l个学生得到的传单总数-前l-1个学生得到的传单总数得到第l个学生(编号为l)得到的传单总数(奇数) } return 0; }
运行截图: