题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4768
题目意思:
有n个A、B、C,每个Ai,Bi,Ci,对于每个P=Ai+k*Ci(P<=Bi,k为整数) 标记一次,如果存在,数据保证只有一个数被标记了奇数次,求出那个数并输出次数。
解题思路:
二分答案。统计区间内所有数的出现次数,根据奇数+偶数=奇数原则。只有一个数是奇数,来决定移动的方向。
最大的n可能有2^31,暴力肯定不行,但网赛的时候很多人都是抑或o(2^31)*n过得,无语了。
二分要求的那个数,对于0~mid段,如果该区间内所有数一共出现了奇数次,则只可能在前面一段,如果是偶数,则肯定在后面一段。每次只用求0~mid该区间所有的值得出现次数和即可,因为奇数减偶数,奇偶性不变。所以每次只用求一遍,然后对于每个mid值,特判一下该点是否为要求的点,如果是的话就找到了。
注意用__int64.
代码:
#include<iostream> #include<cmath> #include<cstdio> #include<sstream> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<set> #include<stack> #include<list> #include<queue> #include<ctime> #include<bitset> #define eps 1e-6 #define INF 0x3f3f3f3f #define PI acos(-1.0) #define ll __int64 #define LL long long #define lson l,m,(rt<<1) #define rson m+1,r,(rt<<1)|1 #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; #define Maxn 21000 ll A[Maxn],B[Maxn],C[Maxn],n; ll iscan(ll m,ll &sum)//sum是区间0~m中所有数出现次数的总和 { ll res=0; //res表示数m出现的次数 sum=0; for(int i=1;i<=n;i++) { ll tmp=min(B[i],m); if(tmp>=A[i]) sum+=(tmp-A[i])/C[i]+1; if(m>B[i]||m<A[i]) //都不行 注意 m<A[i] continue; if((m-A[i])%C[i]==0) //注意m是一个数 res++; } return res; } int main() { while(~scanf("%I64d",&n)) { ll Max=0; for(int i=1;i<=n;i++) { scanf("%I64d%I64d%I64d",&A[i],&B[i],&C[i]); Max=max(Max,B[i]); } ll l=0,r=Max,mid; ll ans=-1,aa; while(l<=r) { mid=(l+r)>>1; //printf("%I64d,%I64d %I64d ",l,r,mid); ll tmp=0; aa=iscan(mid,tmp); //printf("%I64d %I64d ",aa,tmp); //system("pause"); if(aa&1) //mid这个数符不符合 { ans=mid; break; } if(tmp&1) //前一段区间为存在某数为奇数个 r=mid-1; else l=mid+1; } if(ans==-1) { printf("DC Qiang is unhappy. "); } else printf("%I64d %I64d ",ans,aa); } return 0; }