第一次在一个OJ上发了篇题解,没想到他们没有题解审核机制,直接就出现在题解区了.
https://www.acwing.com/solution/content/27658/
那里的markdown在这里无效,我调了一下再发到这里保存一下.
二分
时间复杂度 O(nlogn)
蓝书而来.
本题重点在于满足条件的位置只会不存在或者有且仅有一个,而这个点有一个特殊性质:人数是奇数.
设想:在连续的一段位置上,如果每个位置上的人数加起来为偶数,那么这一段里面不可能存在一个奇数.
如果人数加起来为奇数,那么唯一一个人数为奇数的位置必然在这一段上,利用这个特性可以二分求解.
注意对一段位置上的人数统计是可以写出O(n)方法的,复杂度太高了会TLE.具体实现请结合代码注释理解.
另外,不开long long见祖宗.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int T, n; struct S{ long long s, d, e; }t[200010]; long long cnt(long long l, long long r){ // 统计区间[l, r]上的人数并返回 long long ct = 0; long long begin, end; // 我们要统计的区间和输入数据的每一个区间会出现包含,被包含,交集为空等各种情况,需要对这些情况判断一下并找出等价的[begin, end]区间来处理,也就是说把原来的s,d,e转化为等价的begin,d,end for(int i = 1; i <= n; i++){ // 对每一个区间依次处理 long long s = t[i].s, e = t[i].e, d = t[i].d; // 只是换一个方便的变量名 if(e < l || s > r) continue; // 区间没有交集,跳过 if(l <= s) begin = s; // s在[l,r]之内,则以s为起始点 else { // s < l,那么需要找到最小的k,使得s+k*d>=l,并以s+k*d为起始点 long long k = (l - s - 1) / d + 1; // 由s+k*d>=l得k>=(l-s)/d,稍微思考一下就发现我们需要对右式向上取整,想要对N/M向上取整,表达式为(N-1)/M +1 begin = s + k * d; } end = min(r, e); // 这里对end的处理是显然的 if(end - begin < 0) continue; ct += (end - begin) / d + 1; // 注意当end与bgin相等时人数一定是1 } return ct; } int main(){ scanf("%d", &T); while(T--){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%lld%lld%lld", &t[i].s, &t[i].e, &t[i].d); long long l = 0, r = 2147483648LL; // 注意有一个数据点刚好是int最大值,这里再加一保险一下 bool bad = true; while(l < r && l >= 0){ long long mid = l + r >> 1; if(cnt(l, mid) & 1) r = mid, bad = false; else l = mid + 1; } if(bad) puts("Poor QIN Teng:("); else printf("%lld %lld ", l, cnt(l, l)); } return 0; }