[USACO 2014 Mar Silver]Fair Photography

14 篇文章 0 订阅
2 篇文章 0 订阅

[题目描述]

在X的非负轴上有N个不在同一位置上的数,0或1.至少有1个0.
可以先任意把0染成1.
区间长度定义为,[L,R]中最右和最左的数的差的绝对值.
求一个最长区间,满足区间中所有数0和1的个数相同.
输出这个最长区间的长度.


一看到这道题就想到毛嘉怡那道很漂亮的前缀和应用。。

所以觉得是前缀和。。

对于每一个白牛,+1,对于每一个黑牛,-1。

这样我们只要寻找一个i,j使d[i]<=d[j] && (j-i) mod 2=0 && j-i最大。

想了想想出了一个很鬼畜的做法。。

我们枚举i,然后找到d[j]>=d[i]的最大j,可以使用RMQ来搞。。

为了满足(j-i) mod 2=0,两位一压压两次,第一次压奇数,第二次压偶数。。

觉得至少要200行+,所以很弃疗地看了yyl大神的题解。。

他的想法是这样:先建一棵线段树,线段树里的下标代表是前缀和的值,线段树里维护的东西是这个前缀和区间里最大的pos。

可能有点讲不清楚,就是说比如[1..4]这个节点的data是9,就代表满足1<=d[i]<=4的i最大是9;

从右向左扫描,扫到i的时候,为了查询一个d[j]>=d[i]的最大j,在(d[i],+maxlongint)中查找一个最大值,然后再把这个值插入。

为了满足(j-i) mod 2=0,建两棵线段树。

我觉得这里最巧妙的是打破了原来的常规,把线段树倒了过来。

比如对于一个数组i,d[i]。

正常的线段树是把i当做下标,d[i]当做节点里的值。

这个线段树是把d[i]当做下表,i当做节点里的值。

Code:

const shuru='fairphoto.in';
	  shuchu='fairphoto.out';
	  maxn=100001;
	  INF=1 shl 28;
type node=record
		left,right,data:longint;
		  end;
var	f,a,color:array[0..maxn] of longint;
	t,ans,i,j,k,n:longint;
	tree:array[0..1] of array[0..maxn*6] of node;
	ch:char;
procedure init;
begin
	readln(n);
	for i:=1 to n do
	begin
		read(a[i]); read(ch); read(ch);
		if ch='W' then color[i]:=1
				  else color[i]:=-1;
	end;
end;
procedure qsort(left,right:longint);
var i,j,mid:longint;
begin
	i:=left; j:=right; mid:=a[(i+j) shr 1];
	repeat
		while a[i]<mid do inc(i);
		while a[j]>mid do dec(j);
		if i<=j then begin
						t:=a[i]; a[i]:=a[j]; a[j]:=t;
						t:=color[i]; color[i]:=color[j]; color[j]:=t;
						inc(i); dec(j);
					 end;
	until i>j;
	if j>left  then qsort(left,j);
	if i<right then qsort(i,right);
end;
procedure build(x,l,r:longint);
var mid:longint;
begin
	tree[1][x].left:=l; tree[1][x].right:=r;
	tree[0][x].left:=l; tree[0][x].right:=r;
	if r>l then begin
					mid:=(l+r) shr 1;
					build(x shl 1,l,mid);
					build(x shl 1+1,mid+1,r);
				  end;
end;
function max(a,b:longint):longint;
begin
	if a>b then exit(a);
	exit(b);
end;
procedure forever(sign,x,k,m:longint);
var mid:longint;
begin
	if tree[sign][x].left=tree[sign][x].right then begin
														tree[sign][x].data:=max(tree[sign][x].data,m);
														exit;
												   end;
	mid:=(tree[sign][x].left+tree[sign][x].right) shr 1;
	if k<=mid then forever(sign,x shl 1,k,m)
		      else forever(sign,x shl 1+1,k,m);
	tree[sign][x].data:=max(tree[sign][x shl 1].data,tree[sign][x shl 1+1].data);
end;
function ever(sign,x,l,r:longint):longint;
var mid:longint;
begin
	ever:=0;
	if (l<=tree[sign][x].left) and (r>=tree[sign][x].right) then exit(tree[sign][x].data);
	mid:=(tree[sign][x].left+tree[sign][x].right) shr 1;
	if l<=mid then ever:=ever(sign,x shl 1,l,r);
	if r>mid  then ever:=max(ever,ever(sign,x shl 1+1,l,r));
end;
procedure main;
begin
	init;
	qsort(1,n);
	build(1,1,2*n+1);
	for i:=1 to n do f[i]:=f[i-1]+color[i];
	for i:=n downto 1 do
	begin
		ans:=max(ans,ever(i and 1,1,f[i]+n+1,n shl 1+1)-a[i+1]);
		forever(i and 1,1,f[i]+n+1,a[i]);
	end;
	ans:=max(ans,ever(0,1,n+1,n shl 1+1)-a[1]);
	writeln(ans);
end;
begin
	main;
end.


extra

lsdsjy:"此题大概是NOIP T2难度"

妈蛋是vijos noip模拟赛T2难度么。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值