BZOJ_4320_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
A 3
A 5
B 6
A 9
B 4
Sample Output
3
1
这种求的东西很奇怪的题要想到根号分治。
考虑小于$\sqrt y$这部分答案每次可以暴力更新,设g[i]为当y为i时的答案。
大于$\sqrt y$的这部分相当于有若干后缀,在每个后缀里找最小值,一共最多不超过$\sqrt y$个后缀,那么我们可以$O(\sqrt y)$修改$O(1)$查每个后缀。
具体地,更新时用x更新这个块左端点到x的信息和前面那些块的信息。
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
using namespace std;
#define N 3000050
#define RR register
int M=550;
int pos[N],L[N],f[N],g[N],tag[N],n=3000000;
inline char nc() {
static char buf[100000],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd() {
RR int x=0; RR char c=nc();
while(c<'0'||c>'9') c=nc();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=nc();
return x;
}
inline char rc() {
RR char c=nc();
while(c!='A'&&c!='B') c=nc();
return c;
}
char pbuf[10000000] , *pp = pbuf;
inline void write(int x) {
static int sta[35];
RR int top = 0;
if(!x)sta[++top]=0;
while(x) sta[++top] = x % 10 , x /= 10;
while(top) *pp ++ = sta[top -- ] ^ '0';
}
void add(int x) {
int i;
for(i=1;i<=M;i++) {
g[i]=min(g[i],x%i);
}
for(i=L[pos[x]];i<=x;i++) f[i]=min(f[i],x);
for(i=pos[x]-1;i>=0;i--) tag[i]=min(tag[i],x);
}
int ask(int x) {
if(!x) x++;
return min(tag[pos[x]],f[x]);
}
int solve(int x) {
if(x<M) return g[x];
int t=N,i,j;
for(i=0,j=x;i<=n;i=j,j+=x) {
if(j>n) j=n+1;
int y=ask(i);
if(y<j) t=min(t,y-i);
}
return t;
}
int main() {
int i;
for(i=1;i<=n;i++) pos[i]=i/M,f[i]=N;
for(i=n;i;i--) L[pos[i]]=i;
for(i=1;i<M;i++) g[i]=N;
for(i=0;i<=n/M;i++) tag[i]=N;
int q; q=rd();
int x;
while(q--) {
char opt=rc(); x=rd();
if(opt=='A') {
add(x);
}else {
write(solve(x)); *pp++='\n';
}
}
fwrite(pbuf,1,pp-pbuf,stdout);
}