zoukankan      html  css  js  c++  java
  • 线段树分治学习笔记

    线段树分治是以时间点为下标,将修改以区间形式存储在线段树上,通过可撤销的数据结构进行单点查询(对于某些空间较小,难以实现撤销操作的数据结构如线性基可直接保存副本)。

    模板:洛谷P5787

    用按秩合并的带权并查集维护连通块中点之间边数的奇偶性来判断是否存在负环(用栈储存撤销操作,复杂度 (O(nlog^2{n}))

    #include<bits/stdc++.h>
    #define IL inline
    #define ls k<<1
    #define rs k<<1|1
    #define pb push_back
    #define LL long long
    using namespace std;
    const int N=1e5+3;
    struct hh{
    	int x,y;
    	};
    struct kk{
    	int x,y,op;
    	};
    int n,m,k,dep[N],fa[N],dis[N];
    IL int in(){
    	char c;int f=1;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-') f=-1;
    	int x=c-'0';
    	while((c=getchar())>='0'&&c<='9')
    		x=x*10+c-'0';
    	return x*f;
    	}
    IL int find_f(int x){if(x==fa[x]) return x;return find_f(fa[x]);}
    IL int find_d(int x){if(x==fa[x]) return dis[x];return find_d(fa[x])^dis[x];}
    struct segment{
    	vector<hh>a[N<<2];
    	void mdy(int k,int l,int r,int ll,int rr,int x,int y){
    		if(l>=ll&&r<=rr){a[k].pb((hh){x,y});return;}
    		int mid=l+r>>1;
    		if(ll<=mid) mdy(ls,l,mid,ll,rr,x,y);
    		if(rr>mid) mdy(rs,mid+1,r,ll,rr,x,y);
    		}
    	void solve(int k,int l,int r){
    		vector<kk>s;int mid=l+r>>1;
    		for(int i=0;i<a[k].size();++i){
    			int x=a[k][i].x,y=a[k][i].y;
    			int fx=find_f(x),fy=find_f(y),dx=find_d(x),dy=find_d(y);
    			if(fx==fy){
    				if(dx^dy^1){
    					for(int j=l;j<=r;++j) puts("No");
    					goto dd;
    					}
    				}
    			else{
    				int w=dx^dy^1;
    				if(dep[fx]>dep[fy]) swap(fx,fy),swap(dx,dy);
    				s.pb((kk){fx,fy,dep[fx]==dep[fy]});
    				fa[fx]=fy,dis[fx]=w,dep[fy]+=(dep[fx]==dep[fy]);
    				}
    		}
    		if(l==r) puts("Yes");
    		else solve(ls,l,mid),solve(rs,mid+1,r);
    		dd:for(int i=s.size()-1;~i;--i){
    			int x=s[i].x,y=s[i].y;
    			fa[x]=x,dis[x]=0,dep[y]-=s[i].op;
    			}
    		}
    	}T;
    int main()
    {
    	int x,y,l,r;
    	n=in(),m=in(),k=in();
    	for(int i=1;i<=n;++i) fa[i]=i;
    	while(m--)
    		x=in(),y=in(),l=in()+1,r=in(),
    		T.mdy(1,1,n,l,r,x,y);
    	T.solve(1,1,n);
    	return 0;
    	}
    

    CF1140F Extending Set of Points

    ((x,y)) 转化为连结 (x,y+n) 的边,得到一个二分图,易得该图中一个连通块的贡献为左边点的数量乘右边点的数量(可以将 ((x,y)) 看成坐标帮助理解)。
    并查集维护。

    #include<bits/stdc++.h>
    #define IL inline
    #define ls k<<1
    #define rs k<<1|1
    #define LL long long
    #define pb push_back
    #define M make_pair
    using namespace std;
    const int N=6e5+3;
    int n;
    map<pair<int,int>,int>mp;
    map<pair<int,int>,int>::iterator it;
    struct hh{
    	int fa[N],s1[N],s2[N];
    	int find(int x){return x^fa[x]?find(fa[x]):x;}
    	void set(int n){
    		for(int i=1;i<=2*n;++i) fa[i]=i;
    		for(int i=1;i<=n;++i) s1[i]=1,s2[i+n]=1;
    		}
    	}f;
    struct kk{
    	int x,y;
    	};
    struct segment{
    	vector<kk>a[N<<2];
    	void mdy(int k,int l,int r,int ll,int rr,kk u){
    		if(l>=ll&&r<=rr){a[k].pb(u);return;}
    		int mid=l+r>>1;
    		if(ll<=mid) mdy(ls,l,mid,ll,rr,u);
    		if(rr>mid) mdy(rs,mid+1,r,ll,rr,u);
    		}
    		void solve(int k,int l,int r,LL ans){
    			vector<kk>s;
    			for(int i=0;i<a[k].size();++i){
    				int x=f.find(a[k][i].x),y=f.find(a[k][i].y);
    				if(x^y){
    					ans-=1ll*f.s1[x]*f.s2[x]+1ll*f.s1[y]*f.s2[y];
    					if(f.s1[x]+f.s2[x]>f.s1[y]+f.s2[y]) swap(x,y);
    					f.fa[x]=y,f.s1[y]+=f.s1[x],f.s2[y]+=f.s2[x];
    					ans+=1ll*f.s1[y]*f.s2[y],s.pb((kk){x,y});
    					}
    				}
    			int mid=l+r>>1;
    			if(l==r) printf("%lld ",ans);
    			else solve(ls,l,mid,ans),solve(rs,mid+1,r,ans);
    			for(int i=s.size()-1;~i;--i){
    				int x=s[i].x,y=s[i].y;
    				ans-=1ll*f.s1[y]*f.s2[y];
    				f.fa[x]=x,f.s1[y]-=f.s1[x],f.s2[y]-=f.s2[x];
    				ans+=1ll*f.s1[x]*f.s2[x]+1ll*f.s1[y]*f.s2[y];
    				}
    			}
    	}T;
    IL int in(){
    	char c;int f=1;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-') f=-1;
    	int x=c-'0';
    	while((c=getchar())>='0'&&c<='9')
    		x=x*10+c-'0';
    	return x*f;
    	}
    int main()
    {
    	int x,y;
    	n=in();
    	for(int i=1;i<=n;++i){
    		x=in(),y=in()+3e5;
    		if(!mp[M(x,y)]) mp[M(x,y)]=i;
    		else T.mdy(1,1,n,mp[M(x,y)],i-1,(kk){x,y}),mp[M(x,y)]=0;
    		}
    	for(it=mp.begin();it!=mp.end();++it)
    		if(it->second) T.mdy(1,1,n,it->second,n,(kk){it->first.first,it->first.second});
    	f.set(3e5);
    	T.solve(1,1,n,0);
    	return 0;
    	}
    

    洛谷P4585

    垃圾题面,毁我青春。

    很明显要用可持久化trie维护。

    将修改作为区间储存发现难以维护可持久化trie。

    考虑将询问作为区间储存,单点修改,每次到一个节点,将这个节点所包含的修改操作放到一起建trie,复杂度 (O(17n)),可以接受。

    总复杂度 (O(17nlogn))。(交了一发结果rk1?

    #include<bits/stdc++.h>
    #define IL inline
    #define LL long long
    #define pb push_back
    #define ls k<<1
    #define rs k<<1|1
    using namespace std;
    const int N=1e5+3;
    int n,m,val[N],rt[N],ch[N*100][2],las[N*100],ans[N],cnt,c1,c2;
    struct hh{
    	int l,r,x,day,d,id;
    	bool operator<(const hh &a) const{
    		return r<a.r;}
    	}q[N];
    struct kk{
    	int s,v;
    	bool operator<(const kk &a) const{
    		return s<a.s;}
    	}b[N],c[N];
    IL int in(){
    	char c;int f=1;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-') f=-1;
    	int x=c-'0';
    	while((c=getchar())>='0'&&c<='9')
    		x=x*10+c-'0';
    	return x*f;
    	}
    IL int new_node(){++cnt,ch[cnt][0]=ch[cnt][1]=las[cnt]=0;return cnt;}
    void ins(int &o,int p,int now,int x,int dep){
    	o=new_node(),las[o]=now;
    	if(dep==-1) return;
    	int op=x>>dep&1;
    	ch[o][op^1]=ch[p][op^1];
    	ins(ch[o][op],ch[p][op],now,x,dep-1);
    	}
    int ask(int o,int now,int x,int dep){
    	if(dep==-1) return 0;
    	int op=x>>dep&1;
    	if(las[ch[o][op^1]]>=now) 
    		return ask(ch[o][op^1],now,x,dep-1)+(1<<dep);
    	return ask(ch[o][op],now,x,dep-1);
    	}
    struct segment{
    	vector<hh>a[N<<2];
    	void mdy(int k,int l,int r,int ll,int rr,hh &u){
    		if(rr<l||ll>r) return;
    		if(l>=ll&&r<=rr){a[k].pb(u);return;}
    		int mid=l+r>>1;
    		if(ll<=mid) mdy(ls,l,mid,ll,rr,u);
    		if(rr>mid) mdy(rs,mid+1,r,ll,rr,u);
    		}
    	void solve(int k,int l,int r){
    		int num=0;cnt=0;
    		for(int i=l;i<=r;++i) b[++num]=c[i];
    		sort(b+1,b+num+1);
    		for(int i=1;i<=num;++i) rt[i]=0,ins(rt[i],rt[i-1],b[i].s,b[i].v,16);
    		for(int i=0;i<a[k].size();++i){
    			int pos=upper_bound(b+1,b+num+1,(kk){a[k][i].r,0})-b-1;
    			if(b[pos].s<a[k][i].l) continue;
    			ans[a[k][i].id]=max(ans[a[k][i].id],ask(rt[pos],a[k][i].l,a[k][i].x,16));
    			}
    		int mid=l+r>>1;
    		if(l==r) return;
    		solve(ls,l,mid),solve(rs,mid+1,r);
    		}
    	}T;
    int main()
    {
    	int op,l,r,x,d;
    	n=in(),m=in();
    	for(int i=1;i<=n;++i) val[i]=in();
    	for(int i=1;i<=m;++i){
    		op=in(),l=in(),r=in();
    		if(op) x=in(),d=in(),q[++c2]=(hh){l,r,x,c1,d,c2};
    		else c[++c1]=(kk){l,r};
    		}
    	for(int i=1;i<=c2;++i) T.mdy(1,1,c1,max(1,q[i].day-q[i].d+1),q[i].day,q[i]);
    	T.solve(1,1,c1),cnt=0;
    	for(int i=1;i<=n;++i) rt[i]=0,ins(rt[i],rt[i-1],i,val[i],16);
    	for(int i=1;i<=c2;++i) ans[i]=max(ans[i],ask(rt[q[i].r],q[i].l,q[i].x,16));
    	for(int i=1;i<=c2;++i) printf("%d
    ",ans[i]);
    	return 0; 
    	}
    

    待补:
    CF938G Shortest Path Queries
    CF603E Pastoral Oddities

  • 相关阅读:
    维护需求与新增需求
    LoadRunner如何在注册业务脚本中设置参数化唯一性
    常用的功能测试方法
    应用代码性能诊断分析之内存泄漏
    (52)ElasticSearch之字符串排序问题
    (51)ElasticSearch之query string查询及copy_to的使用
    (50)ElasticSearch之分页查询中的deep paging问题
    (49)ElasticSearch之多index,多type查询
    (48)ElasticSearch之查询结果分析
    (47)ElasticSearch之bulk语法格式解析
  • 原文地址:https://www.cnblogs.com/yiqiAtiya/p/13951862.html
Copyright © 2011-2022 走看看