zoukankan      html  css  js  c++  java
  • CF1039E Summer Oenothera Exhibition 根号分治,LCT,ST表

    CF1039E Summer Oenothera Exhibition

    LG传送门

    根号分治好题。

    可以先看我的根号分治总结

    题意就是给出长度为(n)的区间和(q)组询问以及一个(w),每次询问一个(k),问最少把一段给定区间划分几次可以满足每一段划分出的子区间的极差不超过(w-k)(以下默认(k)就是(w-k))。

    这题主要有两种写法,一种是(O(n sqrt nlog n))的,一种是(O(n^{ frac 5 3}+n^{ frac 4 3} log n))的(本文中默认了(n)(q)同阶)。

    反正先考虑(n^2)暴力,预处理一个ST表,对于每次询问(O(n))地贪心扫一遍,能划在一个子区间内就划在一个子区间内,这样贪心一定是正确的。

    先讲第一种,比较好想,想到从每个点向从它开始能划到它同一个子区间内的最远的点的右边一个点(定义这个点为(h[j]))一条边,这时我们就想起了弹飞绵羊,可以用LCT维护答案,但是由于每次改变(k)值会可能更改所有点连边情况,的直接这样做是(O(n^2 log n))的,连暴力都不如。考虑根号分块,先把询问离线,按(k)从小到大排个序,这样划分出区间的长度是单调不减的,如果所连的边的长度不超过(sqrt n),就直接用LCT暴力维护,由于边的长度不超过(sqrt n),对于所有点最多删边加边(sqrt n)次,由于每次的复杂度都是(log n)的,所以总的复杂度是(O(n sqrt n log n));如果所要连的边长度超过(sqrt n)了,就不连这条边;这样我们就用LCT维护了一个森林,对于森林里每棵树的贡献都可以(O(1))查询,每次询问时先计算一棵树的贡献,到了树根之后就二分它的(h[j]),跳过去把答案++,再计算(h[j])所在树的贡献。

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #define R register
    #define I inline
    using namespace std;
    const int S=100003,M=17,inf=0x7fffffff;
    char buf[1000000],*p1,*p2;
    I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
    I int rd(){
    	R int f=0; R char c=gc();
    	while(c<48||c>57) c=gc();
    	while(c>47&&c<58) f=f*10+(c^48),c=gc();
    	return f;
    }
    struct query{int k,p,o;}q[S];
    struct splay{int p,d[2],s;}e[S];
    int a[S],b[S],l[S],f[S][M],g[S][M],h[S],m;
    vector<int> v[S];
    I int operator<(query x,query y){return x.k<y.k;}
    I int min(int x,int y){return x<y?x:y;}
    I int max(int x,int y){return x>y?x:y;}
    I int qmn(int x,int y){
    	R int i=l[y-x+1];
    	return min(f[x][i],f[y-(1<<i)+1][i]);
    }
    I int qmx(int x,int y){
    	R int i=l[y-x+1];
    	return max(g[x][i],g[y-(1<<i)+1][i]);
    }
    I int nrt(int x){return e[e[x].p].d[0]==x||e[e[x].p].d[1]==x;}
    I void psu(int x){e[x].s=e[e[x].d[0]].s+e[e[x].d[1]].s+1;}
    I void rtt(int x){
    	R int f=e[x].p,g=e[f].p,b=e[f].d[1]==x,c=e[x].d[!b];
    	if(nrt(f)) e[g].d[e[g].d[1]==f]=x;
    	if(c) e[c].p=f;
    	e[f].p=x,e[x].p=g,e[x].d[!b]=f,e[f].d[b]=c,psu(f);
    }
    I void spl(int x){
    	for(R int f,g;nrt(x);rtt(x)){
    		f=e[x].p,g=e[f].p;
    		if(nrt(f))
    			rtt((e[g].d[1]==f)^(e[f].d[1]==x)?x:f);
    	}
    	psu(x);
    }
    I void acc(int x){
    	for(R int y=0;x;x=e[y=x].p)
    		spl(x),e[x].d[1]=y,psu(x);
    }
    I int frt(int x){
    	while(e[x].d[0])
    		x=e[x].d[0];
    	return x;
    }
    I void cut(int x){acc(x),spl(x),e[e[x].d[0]].p=0,e[x].d[0]=0,psu(x);}
    I int fnd(int x,int k){
    	R int l=x+1,r=m,m;
    	for(m=l+r>>1;l<r;m=l+r>>1)
    		qmx(x,m)-qmn(x,m)>k?r=m:l=m+1;
    	return m;
    }
    int main()
    	R int n=rd(),W=rd(),Q=rd(),p=sqrt(n),i,j,k,t,s,o;
    	for(m=n+1,i=1;i<=n;++i)
    		a[i]=f[i][0]=g[i][0]=rd();
    	a[m]=f[m][0]=g[m][0]=inf,l[1]=0;
    	for(i=2;i<=m;++i)
    		l[i]=l[i>>1]+1;
    	for(i=1,k=2;k<=m;++i,k<<=1)
    		for(j=1;j+k-1<=m;++j)
    			f[j][i]=min(f[j][i-1],f[j+(k>>1)][i-1]),g[j][i]=max(g[j][i-1],g[j+(k>>1)][i-1]);
    	for(i=1;i<=Q;++i)
    		q[i].p=i,q[i].k=W-rd();
    	sort(q+1,q+Q+1);
    	for(i=1;i<=n;++i)
    		h[i]=i,e[i].p=i+1,v[1].push_back(i);
    	for(i=1;i<=Q;++i){
    		for(o=0,t=0,s=v[i].size();t<s;++t){
    			j=v[i][t],cut(j);
    			for(k=h[j]+1;qmx(j,k)-qmn(j,k)<=q[i].k&&k-j<=p&&k<=n;++k);
    			if(k-j>p)
    				b[j]=1;
    			else
    				h[j]=k,e[j].p=h[j],v[lower_bound(q+1,q+1+Q,(query){qmx(j,h[j])-qmn(j,h[j]),0})-q].push_back(j);
    		}
    		for(j=1;;j=fnd(j,q[i].k),++o){
    			if(!b[j])
    				acc(j),spl(j),o+=e[j].s-1,j=frt(j);
    			if(j>n)
    				break;
    		}
    		q[q[i].p].o=o-1;
    	}
    	for(i=1;i<=Q;++i)
    		printf("%d
    ",q[i].o);
    	return 0;
    }
    
    

    注意在实现时有一个重要的细节,删边加边的时候必须二分找它下一个可能改变它(h[j])值的询问,而不能对于每次询问都把序列扫一遍,这样复杂度会被卡成(n^2)。例如下面的操作就是错误的。

        for(i=1;i<=Q;++i){
            o=0;
            for(j=1;j<=n;++j)
                if(!b[j]){
                    for(k=h[j];qmx(j,k)-qmn(j,k)<=q[i].k&&k-j<=p&&k<=n;++k);
                    if(k-j>p)
                        b[j]=1,cut(j);
                    else
                        if(k^h[j])
                            cut(j),h[j]=k,e[j].p=h[j];
                }
            for(j=1;;j=fnd(j,q[i].k),++o){
                if(!b[j])
                    acc(j),spl(j),o+=e[j].s-1,j=frt(j);
                if(j>n) break;
            }
            q[q[i].p].o=o-1;
        }
    

    下面介绍第二种做法。

    wxh太神了我看不懂。

    然后Itst把wxh的做法写出来了,我就直接丢链接了。

  • 相关阅读:
    Python3学习笔记(十七):requests模块
    fiddler(四)、断点(转)
    fiddler(三)、会话框添加显示请求方法栏
    PostgreSQL12同步流复制搭建-同步不生效的问题、主库恢复后,无法与新主库同步问题
    PostgreSQL的count(*) count(1) count(列名)的区别
    CentOS系统日志(转)
    常用PostgreSQL HA(高可用)工具收集
    转-性能优化中CPU、内存、磁盘IO、网络性能的依赖
    PostgreSQL查询数据库中包含某种类型的表有哪些
    PostgreSQL中with和without time zone两者有什么区别
  • 原文地址:https://www.cnblogs.com/cj-chd/p/10127491.html
Copyright © 2011-2022 走看看