poj 3145 Harmony Forever

Description

We believe that every inhabitant of this universe eventually will find a way to live together in harmony and peace; that trust, patience, kindness and loyalty will exist between every living being of this earth; people will find a way to appreciate and cooperate with each other instead of continuous bickering, arguing and fighting. Harmony — the stage of society so many people dream of and yet it seems so far away from now…

Fortunately, the method of unlocking the key to true Harmony is just discovered by a group of philosophers. It is recorded on a strange meteorite which has just hit the earth. You need to decipher the true meaning behind those seemingly random symbols… More precisely, you are to write a program which will support the following two kinds of operation on an initially empty set S:

  1. B X: Add number X to set S. The Kth command in the form of B X always happens at time K, and number X does not belong to set S before this operation.
  2. A Y : Of all the numbers in set S currently, find the one which has the minimum remainder when divided by Y. In case a tie occurs, you should choose the one which appeared latest in the input. Report the time when this element is inserted.

It is said that if the answer can be given in the minimum possible time, true Harmony can be achieved by human races. You task is to write a program to help us.

Input

There are multiple test cases in the input file. Each test case starts with one integer T where 1 ≤ T ≤ 40000. The following T lines each describe an operation, either in the form of “B X” or “A Y ” where 1 ≤ X ≤ 500 000, 1 ≤ Y ≤ 1 000 000.

T = 0 indicates the end of input file and should not be processed by your program.

Output

Print the result of each test case in the format as indicated in the sample output. For every line in the form of “A Y”, you should output one number, the requested number, on a new line; output -1 if no such number can be found. Separate the results of two successive inputs with one single blank line.

Sample Input

5
B 1
A 5
B 10
A 5
A 40
2
B 1
A 2
0

Sample Output

Case 1:
1
2
1

Case 2:
1

Source

 
题意:初始一个空集,定义B操作向空集中加入一个数,插入时间是集合中原有元素总数加一,A操作询问集合中mod一个给定数结果最小的数的时间(取模相同的取最晚放入集合的元素)并输出。
//=============================================================================================

题解:我们可以开一个x[i]大小的数组q,每一次插入的时候,更新这个数组中的每一个值,询问的时候就直接输出q[x[i]]即可。但这道题中x[i]的大小为50000,O(N²)的算法承受不了,那怎么办呢?用分段的思想解决,开一个长度为MAX(x[i])的数组c,每插入一个数,就在数组c中标记已插入,询问操作的时候分别查找1~x[i]-1,x[i]~2*x[i]-1,2*x[i]~3*x[i]-1, ……,k*x[i]~max(x[i]),这些段中取模值最小的数,时时更新答案。在查找段最小值的时候,可以利用树状数组或者线段树两种数据结构,把时间复杂度降到O(logMAX(x[i]))。但是,在x[i]很小的时候,时间复杂度就降到了O(N)(还有常数时间。。。)。所以只用线段树也是过不了的。那么就综合两种算法的优点,每次询问的时候,如果x[i]小于lim,则到q数组中直接输出答案,大于lim的询问进行分段查找,每次插入两个数组q和c即可。然后调整一下lim的值,就可以有很好的时效表现了。

 

AC CODE

 

//我用的是树状数组的做法,常数小。(虽然依然很慢。。。)

 

program pku_3145;

const lim=1000;

      max=500000;    //在线做法把数组开到最大。。时间就定死了。。。

var q,c,a,t:array[1..max] of longint;

    i,tot,time,all,x:longint;

    command:char;

//============================================================================

procedure ins(x:longint);    //插入操作。

var j:longint;

begin

  inc(time); t[time]:=x; a[x]:=time;

  for j:=1 to lim do

    if q[j]=0 then q[j]:=time else

    if x mod j<=t[q[j]] mod j then q[j]:=time;

  j:=x;

  while j<=max do

  begin

    inc(c[j]);

    j:=j+j and (-j);

  end;

end;

//============================================================================

function ask(x:longint):longint;    //树状数组求前n项和。

begin ask:=0;

  while x>0 do

  begin

    inc(ask,c[x]);

    x:=x-x and (-x);

  end;

end;

//============================================================================

function askmin(be,en:longint):longint;    //查找每段的最小值。

var l,r,mid,pre,now:longint;

begin

  if be=0 then l:=1 else l:=be;

  if en>max then r:=max else r:=en;

  pre:=ask(l-1); askmin:=-1;

  repeat    //二分查找。

    mid:=(l+r) shr 1;

    now:=ask(mid);

    if now>pre then

    begin

      r:=mid-1;

      askmin:=mid;    //找到一个值才更新,改动左端点不更新。(这里困了好久T.T)

    end else l:=mid+1;

  until l>r;

end;

//============================================================================

procedure enquiry(x:longint);    //询问操作。

var ans,l,r,k:longint;

begin

  if time=0 then writeln('-1') else    //空集输出-1。

  if x<=lim then writeln(q[x]) else    //小的询问直接输出。

  begin

    ans:=x-1;

    l:=0; r:=x-1;    //分段。

    while l<=max do

    begin

      k:=askmin(l,r);

      if (k>0) and ((k mod x<ans mod x) or

      ((k mod x=ans mod x) and (a[k]>a[ans]))) then ans:=k;

      l:=l+x; r:=r+x;

    end; writeln(a[ans]);

  end;

end;

//============================================================================

begin

  while true do

  begin

    readln(tot); time:=0;

    if tot=0 then exit;

    inc(all); writeln('Case ',all,':');

    fillchar(q,sizeof(q),0);

    fillchar(c,sizeof(c),0);

    fillchar(a,sizeof(a),0);

    fillchar(t,sizeof(t),0);    //清除冗余数据。

    for i:=1 to tot do

    begin

      readln(command,x);

      if command='B' then ins(x) else enquiry(x);

    end; writeln;

  end;

end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值