zoukankan      html  css  js  c++  java
  • 6.6 省选模拟赛 线段 二维数点问题 树套树 CDQ分治

    LINK:线段

    还是太菜了 没看出这道题真正的模型 我真是一个典型的没脑子选手。

    考虑如何查询答案。

    每次在一个线段x的状态被更改后 可以发现有影响的是 和x相连那段极长连续1子段。

    设这个子段左端点为l 右端点为r 那么容易发现 左端点为 l~x 右端点为 x~r 这些询问的贡献将会变化。

    将这个变化映射到二维平面上 那么每次询问就是询问某个点的类似的权值。

    考虑一条线段在 T1时刻是联通的 T2时刻不连通了 那么对答案的贡献为T2-T1.

    至此每次修改都可以看成给二维平面上某个区域加上一个值。

    那么对于询问 考虑当前点是否是联通的 如果不连通那就是当前点权 如果联通就是i+当前点权 因为只有不连通的时候贡献才会产生 此时强制性让其不连通即可。

    那么就是一个二维数点问题。经典模型是CDQ分治解决。

    当然一个比较无脑的实现是 线段树套线段树 或者 树状数组套线段树。

    当然内层线段树都需要是 动态开点+标记永久化。下传懒标记对空间开销过大。

    值得一提的是查最远和左右端点的问题 如果采用线段树上二分感觉比往常的要繁琐很多 所以采用的是树状数组+二分 当然也可以二分+set等等。

    这里用的是CDQ分治.

    const int MAXN=300010;
    int n,Q,cnt;
    char a[MAXN];
    int c[MAXN],ans[MAXN];ll s[MAXN];
    struct wy{int x,y,id;int op;}t[MAXN*4],tmp[MAXN*4];
    inline int ask(int x){int cnt=0;while(x)cnt+=c[x],x-=x&-x;return cnt;}
    inline void add(int x,int y){while(x<=n)c[x]+=y,x+=x&-x;}
    inline ll ask1(int x){ll cnt=0;while(x)cnt+=s[x],x-=x&-x;return cnt;}
    inline void add1(int x,int y){while(x<=n)s[x]+=y,x+=x&(-x);}
    inline int check(int l,int r){return ask(r)-ask(l-1)>=r-l+1;}
    inline int askr(int x)
    {
    	if(x==n)return x;
    	if(!check(x+1,x+1))return x;
    	int l=x+1,r=n;
    	while(l+1<r)
    	{
    		int mid=(l+r)>>1;
    		if(check(x+1,mid))l=mid;
    		else r=mid;
    	}
    	if(check(x+1,r))return r;
    	return l;
    }
    inline int askl(int x)
    {
    	if(x==1)return x;
    	if(!check(x-1,x-1))return x;
    	int l=1,r=x-1;
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid,x-1))r=mid;
    		else l=mid+1;
    	}
    	return r;
    }
    inline void CDQ(int l,int r)//第一维时间 第二维横坐标 第三维纵坐标.
    {
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	CDQ(l,mid);
    	CDQ(mid+1,r);//两边都按照横坐标排好序.
    	int i=l,j=mid+1,mark=r;
    	rep(l,r,k)
    	{
    		if(i<=mid&&j<=r)
    		{
    			if(t[i].x<=t[j].x)
    			{
    				if(op(i))add1(t[i].y,op(i));
    				tmp[k]=t[i];++i;
    			}
    			else
    			{
    				if(!op(j))ans[id(j)]+=ask1(t[j].y);
    				tmp[k]=t[j];++j;
    			}
    		}
    		else
    		{
    			mark=min(mark,i-1);
    			if(i<=mid)tmp[k]=t[i],++i;
    			else 
    			{
    				if(!op(j))ans[id(j)]+=ask1(t[j].y);
    				tmp[k]=t[j];++j;mark=mid;
    			}
    		}
    	}
    	rep(l,mark,i)if(op(i))add1(t[i].y,-op(i));
    	rep(l,r,k)t[k]=tmp[k];//归并结束.
    }
    int main()
    {
    	freopen("1.in","r",stdin);
        //freopen("segment.out","w",stdout);
    	gt(n);gt(Q);gc(a);
    	rep(1,n,i)
    	{
    		c[i]+=a[i]-'0';
    		if((i+(i&-i))<=n)c[i+(i&-i)]+=c[i];
    	}
    	rep(1,Q,i)
    	{
    		gc(a);ans[i]=-1;
    		if(a[1]=='q')
    		{
    			int l,r;gt(l),gt(r);--r;
    			int wr=askr(l-1);ans[i]=0;
    			if(wr>=r)ans[i]+=i;//此时是联通的 需要强制断开.
    			t[++cnt]=(wy){l,r,i,0};//查询(1,1) 到 (l,r)的矩形和.
    		}
    		else
    		{
    			int x;gt(x);
    			int wl=askl(x);
    			int wr=askr(x);
    			int ww=check(x,x);
    			if(ww)//当前是联通的.
    			{
    				t[++cnt]=(wy){wl,x,i,i};
    				if(wr+1<=n)t[++cnt]=(wy){wl,wr+1,i,-i};
    				if(x+1<=n)t[++cnt]=(wy){x+1,x,i,-i};
    				if(wr+1<=n)t[++cnt]=(wy){x+1,wr+1,i,i};
    				add(x,-1);
    			}
    			else
    			{
    				t[++cnt]=(wy){wl,x,i,-i};
    				if(wr+1<=n)t[++cnt]=(wy){wl,wr+1,i,i};
    				if(x+1<=n)t[++cnt]=(wy){x+1,x,i,i};
    				if(wr+1<=n)t[++cnt]=(wy){x+1,wr+1,i,-i};
    				add(x,1);
    			}
    		}
    	}
    	CDQ(1,cnt);
    	rep(1,Q,i)if(ans[i]!=-1)put(ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    linux centos 8 为.net core 添加进程守护 Supervisor
    vue-quill-editor安装及使用:自定义工具栏和自定义中文字体,把字体写在html的style中
    nginx参数
    k8s pod 挂载glusterfs报错
    Vue跨域问题解决
    CSS导航菜单(二级菜单)
    CSS导航菜单(一级菜单)
    微信小程序开发正常,真机预览模式错误
    uniapp获取用户OpenId及用户详情
    uniapp 获取用户手机号
  • 原文地址:https://www.cnblogs.com/chdy/p/13060997.html
Copyright © 2011-2022 走看看