hdu Harmony Forever (线段树 )

Problem 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 , Y<=500000 .

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
 


题意:B操作:把数加到S集合,若s中有则不加,加入的编号为第几个加入。

 A操作:询问集合modx的最小树


思路:用线段树记录区间最小值,分段查寻。

代码:

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1

#define pi acos(-1.0)
#define eps 1e-9
#define maxn 10010
#define MOD 1000000007


const int mod = 1e9 +7;
const int INF = 100000000;


struct Tree
{
    int l,r;
    int mi;
} tree[500555*4 + 100];

void pushup(int rt)
{
    tree[rt].mi = min( tree[L(rt)].mi , tree[R(rt)].mi );
}

void build(int rt,int l,int r)
{
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r)
    {
        tree[rt].mi = INF;
        return ;
    }
    int mid = (l+r)>>1;

    build(L(rt),l,mid);
    build(R(rt),mid+1,r);
    pushup(rt);
}

void update(int rt,int l,int r)
{
    if(tree[rt].l>=l && tree[rt].r<=r)
    {
        tree[rt].mi = min(tree[rt].mi,l);
        return;
    }

    int mid = (tree[rt].l+ tree[rt].r)>>1;

    if(mid>=l) update(L(rt),l,r);
    else update(R(rt),l,r);

    pushup(rt);
}

int query(int rt,int l,int r)
{
    if(tree[rt].l>=l && tree[rt].r<=r)
    {
        return tree[rt].mi;
    }

    int mid = (tree[rt].l + tree[rt].r)>>1;

    int ans1=INF,ans2=INF;

    if(mid>=l)
        ans1 = query(L(rt),l,r);

    if(mid<r)
        ans2 = query(R(rt),l,r);

    return min(ans1,ans2);

}


int T;

int n;

int num[500550];
int pos[500550];
int cnt=0;


int main()
{
    char ch[5];
    int a;

    int ans,re;
    int cas=1;

    while(scanf("%d",&n)!=EOF)
    {

        if(n==0) break;
        if(cas!=1)
            printf("\n");

        cnt=0;

        build(1,1,500050);

        memset(pos,-1,sizeof(pos));

        printf("Case %d:\n",cas++);

        for(int i=0; i<n; i++)
        {
            scanf("%s%d",&ch,&a);

            if(ch[0]=='A')
            {
                ans=INF;
                int x =a;
                if(x < 5000)
                {
                    ans = INF;
                    int tx = x;
                    for(int i = cnt - 1; i >= 0; i--)
                    {
                        if(num[i] % x < tx)
                        {
                            tx = num[i] % x;
                            ans = num[i];
                            re = pos[num[i]];
                        }
                        if(tx == 0) break;
                    }
                    if(ans == INF) printf("-1\n");
                    else printf("%d\n",re+1);
                }
                else
                {
                    int all = 500050 / a;

                    for(int j=0; j<=all; j++)
                    {
                        int left = min(j*a,500005);
                        if(left<0) left=1;

                        int right = min(j*a+a-1,500005);

                        int small=query(1,left,right);

                        if(small==INF) continue;

                        int time = pos[small];

                        if(small%a <ans)
                        {
                            ans = small%a;
                            re = time;
                        }
                        else if(small%a==ans)
                        {
                            re = max(re,time);
                        }

                    }
                    if(ans!=INF) printf("%d\n",re+1);
                    else printf("-1\n");
                }

            }
            else
            {
                if(pos[a]!=-1) continue;

                num[cnt]=a;
                pos[a]=cnt++;

                update(1,a,a);
            }
        }
    }
    return 0;
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值