zoukankan      html  css  js  c++  java
  • BZOJ.4320.[ShangHai2006]Homework(根号分治 分块)

    BZOJ


    (mathbb{mod})一个数(y)的最小值,可以考虑枚举剩余系,也就是枚举区间([0,y),[y,2y),[2y,3y)...)中的最小值(求后缀最小值也一样)更新答案,复杂度是(O(frac ny))的。注意到(y>sqrt n)时,枚举次数(<sqrt n)

    我们可以对(y)根号分治,设(m=sqrt{V})(V)是值域)。
    (yleq m)时,可以维护一个大小为(m)的桶(s_i)(表示模数为(i)时的(min{a mathbb{mod} i,ain S})),插入一个数时更新所有桶的值,查询时直接输出。
    (y>m)时,枚举([0,y),[y,2y),[2y,3y)...)这些权值区间求最小值,为了方便可以直接求后缀最小值,也就是(0,y,2y...)这些位置的后缀最小值。
    枚举的复杂度是(O(sqrt V))的,怎么(O(1))求一个位置的后缀最小值呢。
    不妨每次插入(a)就对(1sim a)这些位置与(a)(min)。我们对值域分块,这样更新的复杂度是(O(sqrt V))的,查询某个位置的后缀最小值是(O(1))的。
    然后总复杂度就是(O(nsqrt V))了。

    查后缀最小值也就是这个数往后的一个数是多少。因为只有插入,离线之后也可以看成只有删除,右边第一个数是可以并查集维护的。删掉一个数就是合并两个位置。
    复杂度(O(nsqrt Valpha(V))),也可以过。


    //3656kb	1208ms
    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 500000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    const int N=3e5+3,INF=1<<30,size=548;//(int)sqrt(3e5); //BZOJ编译器版本低到这样写会CE? = = 
    //最大值(初值)不能是N!N对一个大数取模后可能还更小。。
    int g[size+3],bel[N],tag[size+3],mn[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now;
    }
    inline char GetOpt()
    {
    	register char c=gc();
    	while(c!='A'&&c!='B') c=gc();
    	return c;
    }
    inline void Add(const int x)
    {
    	for(int i=1; i<=size; ++i) g[i]=std::min(g[i],x%i);
    	for(int i=0,l=bel[x]; i<l; ++i) tag[i]=std::min(tag[i],x);
    	for(int i=bel[x]*size; i<=x; ++i) mn[i]=std::min(mn[i],x);
    }
    inline int Query(const int x)
    {
    	if(x<=size) return g[x];
    	int res=INF;
    	for(int i=0; i<N; i+=x)
    		res=std::min(res,std::min(mn[i],tag[bel[i]])-i);
    	return res;
    }
    
    int main()
    {
    	for(int i=0; i<=size; ++i) g[i]=INF, tag[i]=INF;
    	for(int i=0; i<N; ++i) bel[i]=i/size, mn[i]=INF;
    	for(int Q=read(); Q--; )
    		switch(GetOpt())
    		{
    			case 'A': Add(read()); break;
    			case 'B': printf("%d
    ",Query(read())); break;
    		}
    
    	return 0;
    }
    
  • 相关阅读:
    C# 小算法1
    函数 y=x^x的分析
    随机数
    对拍
    Cube Stack
    Permutation
    一笔画问题
    康托展开&&康托逆展开
    待完成
    小错误 17/8/10
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10326135.html
Copyright © 2011-2022 走看看