4737. 【NOIP2016提高A组模拟8.25】金色丝线将瞬间一分为二

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

5 10
1 1
2 2
3 3
4 4
5 5

Sample Output

4

Data Constraint

这里写图片描述

Hint

这里写图片描述

题解

该题题目大意很明了,只是有些诡异的气氛。
此题第一眼看上去好像很难搞。
但是,我们要相信自己,那么再看第二眼。
我们发现,对于两点距离,可以把x坐标与y坐标分开来看。
什么意思?就是一个点(x,y)与另一个点(a,b)
他们的距离是:|x-a|+|y-b|
这样x坐标与y坐标互不干扰。
我们发现,两点之间的距离是可以分成x坐标与y坐标来求的。
于是,直观的想法——
离散化一下,然后把x坐标与y坐标按照时间,加入离散化之后数据结构(线段树/树状数组)的位置。
这样,每次查询,我们就把x坐标左边与右边的点对x的贡献求出来,y同理,然后再把x与y加入数据结构。
这样子,我们可以在O(n log n*k)(k是常数)的时间复杂度内求出答案。
实则常数很大,你很难卡常数,树状数组则优秀一些,但是还是很难卡过去(时间只有1s)
某些C选手用O3等神奇优化水过,然而P选手只能祝出题人身体健康。

标程

其中一些神奇的判断是针对部分分打的,所以不要误解。

const up=600000;
type
        arr=array[0..up] of longint;
var
        i,j,k,l,n,mx,my,zd,zx:longint;
        now,ans,sum,op,m:int64;
        tree:array[1..4*up,1..2] of int64;
        gs:array[1..4*up,1..2] of longint;
        x,y,jx,jy,bhx,bhy:arr;
        flag:boolean;
function min(x,y:longint):longint;
begin
        if x<y then exit(x);
        exit(y);
end;
function max(x,y:longint):longint;
begin
        if x>y then exit(x);
        exit(y);
end;
procedure insert(x,l,r,p,q,num:longint);
var
        m:longint;
begin
        if l=r then
        begin
                tree[x,q]:=num;
                gs[x,q]:=1;
        end
        else
        begin
                m:=(l+r) div 2;
                if p<=m then insert(x*2,l,m,p,q,num)
                else insert(x*2+1,m+1,r,p,q,num);
                tree[x,q]:=tree[x*2,q]+tree[x*2+1,q];
                gs[x,q]:=gs[x*2,q]+gs[x*2+1,q];
        end;
end;
procedure find(x,l,r,st,en,p:longint);
var
        m:longint;
begin
        if (l=st) and (r=en) then
        begin
                ans:=ans+tree[x,p];
                sum:=sum+gs[x,p];
        end
        else
        begin
                m:=(l+r) div 2;
                if en<=m then find(2*x,l,m,st,en,p)
                else if st>m then find(2*x+1,m+1,r,st,en,p)
                else
                begin
                        find(2*x,l,m,st,m,p);
                        find(2*x+1,m+1,r,m+1,en,p);
                end;
        end;
end;
procedure discretize(var a:arr);
var
        i,j,k,l:longint;
        b,c:arr;
procedure qsort(l,r:longint);
var
        i,j,k,m:longint;
begin
        i:=l;j:=r;
        m:=a[(l+r) div 2];
        repeat
                while a[i]<m do inc(i);
                while a[j]>m do dec(j);
                if i<=j then
                begin
                        k:=a[i];
                        a[i]:=a[j];
                        a[j]:=k;
                        k:=b[i];
                        b[i]:=b[j];
                        b[j]:=k;
                        inc(i);dec(j);
                end;
        until i>j;
        if l<j then qsort(l,j);
        if r>i then qsort(i,r);
end;
begin
        fillchar(b,sizeof(b),0);
        fillchar(c,sizeof(c),0);
        for i:=0 to n do b[i]:=i;
        qsort(0,n);
        c[b[0]]:=0;
        j:=0;
        for i:=1 to n do
        begin
                inc(j);
                c[b[i]]:=j;
        end;
        my:=0;
        j:=0;
        for i:=1 to n do
        begin
                if a[i]<>a[i-1] then
                begin
                        inc(j);
                        inc(my);
                end
        end;
        fillchar(a,sizeof(a),0);
        for i:=0 to n do a[i]:=c[i];
        bhy:=b;
end;
begin
        zx:=maxlongint;
        flag:=false;
        readln(n,m);
        for i:=1 to n do
        begin
                readln(x[i],y[i]);
                zd:=max(zd,x[i]);
                zx:=min(zx,x[i]);
                if (i>1) and ((x[i-1]>x[i]) or (y[i-1]>y[i])) then
                flag:=true;
        end;
        now:=0;
        jx:=x;jy:=y;
        discretize(x);
        bhx:=bhy;
        mx:=my;
        discretize(y);
        if zd=zx then
        begin
                insert(1,1,n,y[1],2,jy[1]);
                for i:=2 to n do
                begin
                        ans:=0;
                        sum:=0;
                        if y[i]-1>0 then
                                find(1,1,n,1,y[i]-1,2);
                        op:=sum*jy[i]-ans;
                        ans:=0;
                        sum:=0;
                        if y[i]+1<=n then
                                find(1,1,n,y[i]+1,n,2);
                        op:=op+ans-sum*jy[i];
                        now:=now+op;
                        if now>m then
                        begin
                                writeln(i);
                                break;
                        end;
                        insert(1,1,n,y[i],2,jy[i]);
                end;
                if now<=m then
                begin
                        writeln(-1);
                end;
        end
        else
        if not flag then
        begin
                insert(1,1,n,x[1],1,jx[1]);
                insert(1,1,n,y[1],2,jy[1]);
                for i:=2 to n do
                begin
                        ans:=0;
                        sum:=0;
                        if x[i]-1>0 then
                                find(1,1,n,1,x[i]-1,1);
                        op:=sum*jx[i]-ans;
                        ans:=0;
                        sum:=0;
                        if y[i]-1>0 then
                                find(1,1,n,1,y[i]-1,2);
                        op:=op+sum*jy[i]-ans;
                        now:=now+op;
                        if now>m then
                        begin
                                writeln(i);
                                break;
                        end;
                        insert(1,1,n,x[i],1,jx[i]);
                        insert(1,1,n,y[i],2,jy[i]);
                end;
                if now<=m then
                begin
                        writeln(-1);
                end;

        end
        else
        begin
                insert(1,1,n,x[1],1,jx[1]);
                insert(1,1,n,y[1],2,jy[1]);
                for i:=2 to n do
                begin
                        ans:=0;
                        sum:=0;
                        if x[i]-1>0 then
                                find(1,1,n,1,x[i]-1,1);
                        op:=sum*jx[i]-ans;
                        ans:=0;
                        sum:=0;
                        if y[i]-1>0 then
                                find(1,1,n,1,y[i]-1,2);
                        op:=op+sum*jy[i]-ans;
                        ans:=0;
                        sum:=0;
                        if x[i]+1<=n then
                                find(1,1,n,x[i]+1,n,1);
                        op:=op+ans-sum*jx[i];
                        ans:=0;
                        sum:=0;
                        if y[i]+1<=n then
                                find(1,1,n,y[i]+1,n,2);
                        op:=op+ans-sum*jy[i];
                        now:=now+op;
                        if now>m then
                        begin
                                writeln(i);
                                break;
                        end;
                        insert(1,1,n,x[i],1,jx[i]);
                        insert(1,1,n,y[i],2,jy[i]);
                end;
                if now<=m then
                begin
                        writeln(-1);
                end;
        end;
end.

这里写图片描述
答案是可以的。
我们换一种思路,我们可以二分一下答案,然后,我们就要判断在这个答案下,满不满足要求即可。
那么我们直观的思路——
二分,然后再把当前答案之前那些点排序,然后计算距离和。
那么我们看看:
由于排完序了,那么这个序列是递增的,于是,我们可以维护3个值——
sum:当前的距离和。
ans:前缀和。
那么设我们当前枚举到第i个点,于是sum+=(i-1)*x[i]-ans
什么意思?由于是递增的(这个很重要),那么我们是之前的x’走(x[i]-x’)的距离对答案贡献。
那么,化简一下就是上面的式子了。

这样就可以在O(n log^2 n*(极小的常数,可以忽略不计))
但是,多了个log很蛋疼。
其实,我们发现,每次在二分里面排序其实很蠢,我们直接在外面排个序,然后我们就可以把每个点的相应位置给记录下来,于是,我们就直接在二分里面用就好了。
时间O(n log n)nice~~~

真·标程

{$inline on}
type
		arr=array[0..10000000]of longint;
var
        i,j,k,l,n:longint;
        m,sum,num,ans:int64;
        x,y,bhx,bhy:arr;

function pd(m:longint):int64;inline;
var
		i:longint;
		ans,sum:int64;
begin
		ans:=0;
		sum:=0;
		for i:=1 to n do
		begin
                if bhx[i]<=n then
				begin
                        inc(sum,(i-1)*x[i]-ans);
                        inc(ans,x[i]);
				end;
		end;
		ans:=0;
        for i:=1 to n do
		begin
                if bhy[i]<=n then
				begin
                        inc(sum,(i-1)*y[i]-ans);
                        inc(ans,y[i]);
				end;
		end;
end;

procedure erfen;inline;
var
		l,r,mid:longint;
begin
		l:=2;
		r:=n;
		while l < r do
		begin
				mid:=(l+r)div 2;
				if pd(mid)>m then
                begin
                        r:=mid;
                end
				else
                begin
                        l:=mid+1;
                end;
		end;
		writeln(l);
end;

procedure qsort(i,j:longint;var x,bh:arr);inline;
var
        l,r,m:longint;
begin
        l:=i;
        r:=j;
        m:=x[(i+j)div 2];
        repeat
                while x[i]<m do inc(i);
                while x[j]>m do dec(j);
                if i<=j then
                begin
                        bh[0]:=bh[i];
                        bh[i]:=bh[j]; 
                        bh[j]:=bh[0];
                        x[0]:=x[i];
                        x[i]:=x[j];
                        x[j]:=x[0];
                        inc(i);
                        dec(j);
                end;
        until i>j;
        if i<r then qsort(i,r,x,bh);
        if l<j then qsort(l,j,x,bh);
end;

begin
        //assign(input,'cut.in');reset(input);
        readln(n,m);
        for i:=1 to n do
        begin
                readln(x[i],y[i]);
        end;
        for i:=1 to n do
        begin
                bhx[i]:=i;
                bhy[i]:=i;
        end;
        qsort(1,n,x,bhx);
        qsort(1,n,y,bhy);
		ans:=0;
		sum:=0;
		for i:=1 to n do
		begin
                if bhx[i]<=n then
				begin
                        inc(sum,(i-1)*x[i]-ans);
                        inc(ans,x[i]);
				end;
		end;
		ans:=0;
        for i:=1 to n do
		begin
                if bhy[i]<=n then
				begin
                        inc(sum,(i-1)*y[i]-ans);
                        inc(ans,y[i]);
				end;
		end;
		if sum<=m then
        begin
                writeln(-1);
        end
        else
        begin
                erfen;
        end;
end.

这里写图片描述
十个表过去,O(1)

我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。
原文地址:https://www.cnblogs.com/RainbowCrown/p/11148394.html