Flyer
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1140 Accepted Submission(s): 397
解题思路:
本题的大致意思是共有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;
}
运行截图: