【二分单个位置 前缀和】 防线

传送门

题意

一维的防线,有(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();
}
原文地址:https://www.cnblogs.com/hhyx/p/12963503.html