• 1004F.Sonya and Bitwise OR(毒瘤线段树+分治思想)


    性质:

    对一个序列的或前缀和是单调不降的,每次变化至少增加一个二进制位

    同时一段区间内发生变化的位置数量<=20(log值域)

    那么就可以对线段树上的每个节点维护这样的一组信息:

    区间内前缀或发生变化的位置以及它在区间内的前缀或

    区间内后缀或发生变化的位置以及它在区间内的后缀或

    一个区间内的答案数量,就是它两个子区间的答案加上区间本身的答案。

    区间本身的答案计算,就是枚举左区间后缀和和右区间前缀和的每个位置,就是用双指针的方法O(20)求解。因为最多20个点。

    现在考虑区间合并。

    两个区间的合并,先保留左区间前缀或的变化位置,然后枚举右区间前缀或的每个位置,如果不变就跳过,如果变化就把位置加到父节点的前缀或变化位置里。

    同理,保留右区间的后缀或变化位置,然后枚举左区间后缀或的每个位置,如果不变就跳过,如果变化就把位置加到父节点的后缀或变化位置里。

    每个区间的答案就是合并后的新答案加上它的两个子区间的答案。

    现在考虑区间查询。

    查询一个区间,返回一个结构体,包含前缀、后缀、答案。

    用刚刚的方法合并两个子查询,并返回上一层函数即可。

    现在考虑单点修改。

    修改一个点,会影响它的所有父节点,对所有父节点的前后缀变化点暴力修改就完事了。

    时间复杂度O(20logn)。

    总的时间复杂度O(20nlogn)。

    感觉这题是我目前遇到的线段树区间维护天花板了。妥妥的银牌题往上,无论是代码难度还是思维难度。

    #include<bits/stdc++.h>
    using namespace std;
    
    inline int read()
    {
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return x*f;
    }
    typedef long long ll;
    const int maxn=2e5+100;
    int n,m,a[maxn],X; 
    struct node {
    	ll ans;
    	vector<pair<int,int> > L,R;
    	void clear () {
    		ans=0;
    		L.clear();
    		R.clear();
    	}
    	node () {
    		clear();
    	}
    }segTree[maxn<<2];
    node operator + (node x,node y) {
    	node ret;
    	ret.L=x.L;
    	for (int i=0,u=ret.L.back().second;i<y.L.size();i++) {
    		pair<int,int> tt=y.L[i];
    		if ((tt.second|u)!=u) {
    			u|=tt.second;
    			ret.L.push_back(make_pair(tt.first,u)); 
    		}
    	}
    	ret.R=y.R;
    	reverse(ret.R.begin(),ret.R.end());
    	for (int i=x.R.size()-1,u=ret.R.back().second;i>=0;i--) {
    		pair<int,int> tt=x.R[i];
    		if ((tt.second|u)!=u) {
    			u|=tt.second;
    			ret.R.push_back(make_pair(tt.first,u));
    		}
    	}
    	reverse(ret.R.begin(),ret.R.end());
    	ret.ans=x.ans+y.ans;
    	for (int i=0,u=0;i<y.L.size();i++) {
    		while (u<x.R.size()&&(x.R[u].second|y.L[i].second)>=X) u++;
    		int ll=(!u?0:x.R[u-1].first-x.L[0].first+1);
    		int rr=(i+1==y.L.size()?y.R.back().first-y.L[i].first+1:y.L[i+1].first-y.L[i].first);
    		ret.ans+=1ll*ll*rr;
    	}
    	return ret;
    }
    void build (int i,int l,int r) {
    	segTree[i].clear();
    	if (l==r) {
    		segTree[i].L.push_back(make_pair(l,a[l]));
    		segTree[i].R.push_back(make_pair(l,a[l]));
    		segTree[i].ans=(a[l]>=X);
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    	segTree[i]=segTree[i<<1]+segTree[i<<1|1];
    }
    void up (int i,int l,int r,int p,int v) {
    	if (l==r) {
    		segTree[i].clear();
    		segTree[i].L.push_back(make_pair(p,v));
    		segTree[i].R.push_back(make_pair(p,v));
    		segTree[i].ans=(v>=X);
    		return;
    	}
    	int mid=(l+r)>>1;
    	if (p<=mid) up(i<<1,l,mid,p,v);
    	if (p>mid) up(i<<1|1,mid+1,r,p,v);
    	segTree[i]=segTree[i<<1]+segTree[i<<1|1]; 
    }
    node query (int i,int l,int r,int L,int R) {
    	if (l>=L&&r<=R) return segTree[i];
    	int mid=(l+r)>>1;
    	if (R<=mid) return query(i<<1,l,mid,L,R);
    	else if (L>mid) return query(i<<1|1,mid+1,r,L,R);
    	else return query(i<<1,l,mid,L,R)+query(i<<1|1,mid+1,r,L,R);
    }
    main (){
    	n=read();
    	m=read();
    	X=read();
    	for (int i=1;i<=n;i++) a[i]=read();
    	build(1,1,n);
    	for (int i=1;i<=m;i++) {
    		int op=read();
    		int x=read();
    		int y=read();
    		if (op==1)
    			up(1,1,n,x,y);
    		else
    			printf("%lld
    ",query(1,1,n,x,y).ans);
    	}
    }
  • 相关阅读:
    DotNet友元程序集解析
    fastadmin如何在列表操作列区域添加按钮及控制已有按钮显示
    PHP合成透明图片
    linux系统下执行定时任务的全过程
    关于阿里云简单文件上传OSS思路整理服务器上的文件上传到OSS
    PHP图片和文字合成函数刚刚出炉
    关于在fastadmin后台AJAX上传图片或者视频增加额外参数的办法
    标记一下关于fastadmin在列表页获取视频时长并且AJAX提交到后端正常显示的过程
    关于phpexcel导出65535的解决思路
    PHP原生代码集成腾讯云对象存储 COS整个过程源码方式
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14815745.html
Copyright © 2020-2023  润新知