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
A 3
A 5
B 6
A 9
B 4
Sample Output
3
1
1
HINT
【样例说明】
在第三行的操作前,集合里有 3、5 两个代号,此时 mod 6 最小的值是 3 mod 6 = 3;
在第五行的操作前,集合里有 3、5、9,此时 mod 4 最小的值是 5 mod 4 = 1;
按权值分块,<=sqrt(300000)的答案直接爆算,>sqrt(300000)的答案可以枚举x/Y的整数部分。
那么问题就是如何快速计算>x的最小的数并带插入。
注意快速计算>x的最小的数并带删除可以用并查集来做,那么时光倒流一下就行了。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=300010; const int maxm=100010; int pa[maxn],vis[maxn]; int A[maxm],type[maxm],res[410],ans[maxm]; inline int findset(int x) {return !pa[x]||x==pa[x]?x:pa[x]=findset(pa[x]);} int main() { int SIZE=400; rep(i,1,SIZE) res[i]=1e9; int n=read(); rep(i,1,n) { char s[2];int x; scanf("%s%d",s,&x); if(s[0]=='A') { type[i]=1;vis[x]=1;A[i]=x; rep(i,1,SIZE) res[i]=min(res[i],x%i); } else { if(x<=SIZE) ans[i]=res[x]; else type[i]=2,A[i]=x; } } rep(i,1,300000) if(!vis[i]) pa[i]=i+1; dwn(i,n,1) if(type[i]) { if(type[i]==1) pa[A[i]]=A[i]+1; else { ans[i]=1e9; for(int j=0;j<=300000;j+=A[i]) { int x=findset(max(1,j)); if(x<=300000) ans[i]=min(ans[i],x%A[i]); } } } rep(i,1,n) if(type[i]!=1) printf("%d\n",ans[i]); return 0; }