【bzoj4320】【ShangHai2006 Homework】【并查集+离线处理】

Description

  1:在人物集合 S 中加入一个新的程序员,其代号为 X,保证 X 在当前集合中不存在。 
  2:在当前的人物集合中询问程序员的mod Y 最小的值。 (为什么统计这个?因为拯救
过世界的人太多了,只能取模) 

Input

第一行为用空格隔开的一个个正整数 N。 
接下来有 N 行,若该行第一个字符为“A” ,则表示操作 1;若为“B”,表示操作 2; 
其中 对于 100%的数据:N≤100000, 1≤X,Y≤300000,保证第二行为操作 1。 

Output

对于操作 2,每行输出一个合法答案。 

Sample Input

5
A 3
A 5
B 6
A 9
B 4

Sample Output

3
1

HINT

【样例说明】 

  在第三行的操作前,集合里有 3、5 两个代号,此时 mod 6 最小的值是 3 mod 6 = 3; 

  在第五行的操作前,集合里有 3、5、9,此时 mod 4 最小的值是 5 mod 4 = 1; 

题解:首先把询问分为Y大于sqrt(300000),和Y小于sqrt(300000),

           用一个数组记录一下1-sqrt(300000)的答案。碰到这样的询问就直接输出。

           对于大于sqrt(300000)的询问,我们离线处理。把加点改成删点。

           每次询问相当于询问当前数集中比Y或Y的倍数大并且距离最近的数。

           删点很显然可以用并查集处理,把空格都连起来(同花神游历各国)。

代码:

          

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300010
#define SN 300
using namespace std;
struct use{int kind,k,ans;}q[N];
int n,mn[SN+10],f[N],fa[N];
char ch[5];
inline int find(int x){if (x!=fa[x]) fa[x]=find(fa[x]);return fa[x];}
int main(){
  scanf("%d",&n);for (int i=1;i<=300001;i++) fa[i]=i;memset(mn,127/3,sizeof(mn));
  for (int i=1;i<=n;i++){
    scanf("%s%d",&ch,&q[i].k);q[i].kind=ch[0]-'A';
    if (q[i].kind==0){for (int j=1;j<=SN;j++) mn[j]=min(mn[j],q[i].k%j);f[q[i].k]=1;}
    if (q[i].kind==1&&q[i].k<=SN) q[i].ans=mn[q[i].k];
  }
  for (int i=1;i<=300000;i++) if (!f[i]) fa[i]=i+1;
  for (int i=n;i>=1;i--){
    if (q[i].kind==0)fa[q[i].k]=q[i].k+1; 
    else{
      int t,mi(N);  
      if (q[i].k>SN){
      for (int j=0;j<=300000;j+=q[i].k){
        t=find(max(j,1));if (t<=300000)mi=min(mi,t%q[i].k);
        }
       q[i].ans=mi;
      }
    }
  } 
  for (int i=1;i<=n;i++) if (q[i].kind)printf("%d\n",q[i].ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值