二分——HDU 4758

题目链接: 

     http://acm.hdu.edu.cn/showproblem.php?pid=4768

题目大意:

    有n个学生组织发传单,每个学生组织包括三个数A,B,C,三个数表示该组织只发传单给第A+ K*C位学生(A+K*C < B),一共有2^31位学生,其中最多有一位学生是“unlucky”的,找出这名学生的序号及他收到的传单数,否则输出“DC Qiang is unhappy.”。

思路分析:

    题目保证最多有一位学生“unlucky”,即最多有一位学生收到奇数张传单,其他 学生都受到偶数张传单,那么:若总传单数为偶数,无“unlucky”学生,否则寻找这位学生(这意味着这位学生一定存在);

    为了提高效率,二分区间来寻找这位学生,这位学生一定在(l, r)区间内,只要不断二分枚举mid,计算出0~mid收到的传单数t,如果t为偶数,那么这位同学一定在区间右侧,否则在区间左侧,直到找到这位学生,然后算出他收到的传单数即可。

题目代码:
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 20010;
struct Node{
	ll A;
	ll B;
	ll C;
}node[MAX]; 

bool solve(ll end, int n){
	ll ans = 0;
	for(int i = 0 ; i < n; i++){
		if(end >= node[i].A){
			ans += ((min(end, node[i].B) - node[i].A)/node[i].C + 1);
		}
	}
	return ans%2;
}
int main(){
	int n;
	ll N = 1;
	for(int i = 1;i <= 31; i++){
		N *= 2;
	}
	while(scanf("%d", &n) != EOF){
		for(int i = 0; i < n; i++){
			scanf("%lld %lld %lld",  &node[i].A, &node[i].B, &node[i].C);
		}
		if(!solve(N, n)){
			cout<<"DC Qiang is unhappy."<<endl;
			continue;
		}
		ll l, r, mid;
		l = 0;
		r = N;
		ll ans1 = 0;
		while(l <= r){
			mid = (l + r)/2;
			bool cnt = solve(mid, n);
			if(cnt){
				r = mid - 1;
				ans1 = mid;
			}
			else{
				l = mid + 1;
			}
		}
		ll ans2 = 0;
		for(int i = 0; i < n; i++){
			if(node[i].B >= ans1 && ans1 >= node[i].A && (ans1 - node[i].A)%node[i].C == 0){
				//cout<<i<<endl;
				ans2++;
			}
		}
		cout<<ans1<<" "<<ans2<<endl;
	} 
} 

猜你喜欢

转载自blog.csdn.net/swin16/article/details/80213209