传送门
题意
一维的防线,有(n)组防具,每组防具通过(s、e、d)来描述,即(s,s+d,s+2d dots , s+kd leq e , s+(k+1)d>e),如果某个位置上防具的个数是偶数个这个防线就是没有破绽的,0也是偶数,有奇数个位置有防具就是破绽,整条防线最多只有一个位置上有破绽,如果有破绽输出破绽位置和这个位置上的防具数量,否则的话输出("There's; no; weakness"),一共有(t)组测试数据.
数据范围
防具的总数不超过(10^{8})
(0<= s_{i}、k_{i}、d_{i} <= 2^{31}-1)
(n<=200000)
题解
通过前缀和来使具有单调性,奇数最多只有一个,所以前缀和一定为奇数,具有了单调性就可以二分位置,当前前缀和为偶则左边一定全是偶,如果存在奇数的位置一定在右半区间
(O(n))时间求出前缀和,二分的位置是(mid),枚举每一个等差数列,如果(sleq mid)则存在交集,共同区间为([s,min(e,mid) ]),则该区间中包含这个数列的个数为
- (leftlfloorfrac{(min (e, mid)-S)}{d} ight floor +1)
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,n) for(int i=a;i<n;i++)
const int N=2e5+10;
ll n;
struct node
{
ll s,e,d;
}a[N];
ll get(ll mid)
{
ll ans=0;
rep(i,1,n+1)
if(a[i].s<=mid)
ans+=(min(mid,a[i].e)-a[i].s)/a[i].d+1;
return ans;
}
void solve()
{
cin >> n;
rep(i,1,n+1)
cin >> a[i].s >> a[i].e >> a[i].d;
ll l=0,r=1e9;
while(l<r){
ll mid=l+r>>1;
if(get(mid)&1)
r=mid;
else
l=mid+1;
}
ll ans=get(r)-get(r-1);
if(ans&1)
cout<<l<<' '<<ans<<endl;
else
puts("There's no weakness.");
}
int main(){
int t;
cin>>t;
while(t--)
solve();
}