金色丝线将瞬间一分为二

题目 

金色丝线将瞬间一分为二_第1张图片

 题解&思路

一.解法1

对于第i个点,其贡献为\sum_{j=1}^{j<i} |x_{i}-x_{j}| + |y_{i} - y_{j}|,对于这个,把x,y分开算,把绝对值打开,就是求有哪些xj比xi小,比xi大,则就可以用树状数组维护一下

 

二.解法2

明显这道题可以直接二分求答案,那么仍然分开算x,y,先将它们从小到大记录排名(排名即原本输入时的顺序)按值排序,那么二分到一个答案x,则在排序后数组找出排名不大于x的找出来得到新数组,则新数组两两求距离即可,又因为排过序,则直接用前缀和维护

#include 
using namespace std;
#define reg register
#define ll long long
const int MAXN = 6e5 + 3;
int n;
ll D;
struct node{
    ll zh;int num;
    friend bool operator < ( node a , node b ){
        return a.zh < b.zh;
    }
}a[MAXN] , b[MAXN];
void read( ll &x ){
	char s = getchar();
	while( s < '0' || s > '9' ){
		s = getchar();
	}
	while( s >= '0' && s <= '9' ){
		x = x * 10 + s - '0';
		s = getchar();
	}
}
ll s[MAXN] , sum[MAXN];
bool check( int x ){
    int ncnt = 0;
    for( int i = 1 ; i <= n ; i ++ )
        if( a[i].num <= x )
            s[++ncnt] = a[i].zh;
    sum[0] = 0;
    ll tot = 0;
    for( int i = 1 ; i <= ncnt ; i ++ ){
        sum[i] = sum[i-1] + s[i];
        tot += 1ll * ( i - 1 ) * s[i] - sum[i-1];
    }
    ncnt = 0;
    for( int i = 1 ; i <= n ; i ++ )
        if( b[i].num <= x )
            s[++ncnt] = b[i].zh;
    sum[0] = 0;
    for( int i = 1 ; i <= ncnt ; i ++ ){
        sum[i] = sum[i-1] + s[i];
        tot += 1ll * ( i - 1 ) * s[i] - sum[i-1];
    }
    if( tot > D ) return 1;
    return 0;
}
int main(){
	//freopen( "hair.in" , "r" , stdin );
    //freopen( "hair.out" , "w" , stdout );
    scanf( "%d" , &n) ;
    read( D );
    int ncnt = 0;
    for( reg int i = 1 ; i <= n ; i ++ ){
        scanf( "%lld%lld" , &a[i].zh , &b[i].zh );
        a[i].num = b[i].num = i;

    }
    sort( a + 1 , a + n + 1 );sort( b + 1 , b + n + 1 );
    int l = 1 , r = n , ans = -1;
    while( l <= r ){
        int mid = ( l + r ) / 2;
        if( check( mid) )
            ans = mid , r = mid - 1;
        else
            l = mid + 1;
    }
    printf( "%d" , ans );
    return 0;
}

 

你可能感兴趣的:(区间,二分)