• [线段树&主席树]の一些题解


    [线段树&主席树]の一些题解

    T1:The Child and Sequence

    (Description:)

    给你一个长度为(n(n leq 10^5))的序列,要求支持区间求和,区间取模,单点修改

    (Solution:)

    区间求和和单点修改都是基本的线段树操作,关键在于区间取模。
    这里我们维护一个区间最大值,这样如果模数大于当前区间的最大值,就不需要再往下走了
    由于(x \% y leqfrac{x}{2}),所以时间复杂度为(O(nlog_2^2 n))
    这样时间效率能够得到保证

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) (x<<1)
    #define rr(x) (x<<1|1)
    using namespace std;
    typedef long long lol;
    lol n,m,a[100005];
    struct segment{
    	lol mmax,sum;
    }sgm[400005];
    void push_up(lol root){
    	sgm[root].mmax=max(sgm[ll(root)].mmax,sgm[rr(root)].mmax);
    	sgm[root].sum=sgm[ll(root)].sum+sgm[rr(root)].sum;
    	return;
    }
    void build(lol root,lol left,lol right){
    	if(left==right){
    		sgm[root].mmax=a[left];
    		sgm[root].sum=a[left];
    		return;
    	}
    	if(left>right)return;
    	lol mid=(left+right)>>1;
    	build(ll(root),left,mid);
    	build(rr(root),mid+1,right);
    	push_up(root);
    }
    void insert_mod(lol root,lol left,lol right,lol l,lol r,lol mod){
    	if(sgm[root].mmax<mod)return;
    	if(left>r||right<l)return;
    	if(left==right){
    		sgm[root].mmax%=mod;
    		sgm[root].sum%=mod;
    		return;
    	}
    	lol mid=(left+right)>>1;
    	insert_mod(ll(root),left,mid,l,r,mod);
    	insert_mod(rr(root),mid+1,right,l,r,mod);
    	push_up(root);
    }
    void insert_point(lol root,lol left,lol right,lol x,lol v){
    	if(left>x||right<x)return;
    	if(left==right){
    		sgm[root].mmax=v;
    		sgm[root].sum=v;
    		return;
    	}
    	lol mid=(left+right)>>1;
    	insert_point(ll(root),left,mid,x,v);
    	insert_point(rr(root),mid+1,right,x,v);
    	push_up(root);
    }
    lol query(lol root,lol left,lol right,lol l,lol r){
    	if(l<=left&&right<=r)return sgm[root].sum;
    	if(right<l||left>r)return 0;
    	lol mid=(left+right)>>1;
    	return query(ll(root),left,mid,l,r)+query(rr(root),mid+1,right,l,r);
    }
    int main(){
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    	build(1,1,n);
    	for(int i=1;i<=m;i++){
    		lol opt,u,v,w;
    		scanf("%lld",&opt);
    		if(opt==1){
    			scanf("%lld%lld",&u,&v);
    			printf("%lld
    ",query(1,1,n,u,v));
    		}
    		else if(opt==2){
    			scanf("%lld%lld%lld",&u,&v,&w);
    			insert_mod(1,1,n,u,v,w);
    		}
    		else if(opt==3){
    			scanf("%lld%lld",&u,&w);
    			insert_point(1,1,n,u,w);
    		}
    	}
    	return 0;
    }
    

    T2:Multiply game

    (Description:)

    给你一个长度为(n(n leq 10^5))的序列,维护区间乘积,支持单点修改

    (Solution:)

    线段树板子。。。

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) (x<<1)
    #define rr(x) (x<<1|1)
    #define mod (1000000007)
    using namespace std;
    typedef long long lol;
    int T,n,m;
    lol a[50005];
    struct segment{
    	lol mul;
    }sgm[200005];
    void push_up(int root){
    	sgm[root].mul=sgm[ll(root)].mul*sgm[rr(root)].mul%mod;
    	return;
    }
    void build(int root,int left,int right){
    	if(left==right){
    		sgm[root].mul=a[left]%mod;
    		return;
    	}
    	if(left>right)return;
    	int mid=(left+right)>>1;
    	build(ll(root),left,mid);
    	build(rr(root),mid+1,right);
    	push_up(root);
    }
    void insert(int root,int left,int right,int l,int r,int x){
    	if(l<=left&&right<=r){
    		sgm[root].mul=x%mod;
    		return;
    	}
    	if(l>right||r<left)return;
    	int mid=(left+right)>>1;
    	insert(ll(root),left,mid,l,r,x);
    	insert(rr(root),mid+1,right,l,r,x);
    	push_up(root);
    }
    lol query(int root,int left,int right,int l,int r){
    	if(l<=left&&right<=r)return sgm[root].mul%mod;
    	if(l>right||r<left)return 1;
    	int mid=(left+right)>>1;
    	return query(ll(root),left,mid,l,r)*query(rr(root),mid+1,right,l,r)%mod;
    }
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d",&n);
    		for(int i=1;i<=n*4;i++)
    			sgm[i].mul=1;
    		for(int i=1;i<=n;i++)
    			scanf("%lld",&a[i]);
    		build(1,1,n);
    		scanf("%d",&m);
    		while(m--){
    			int u,v,w;
    			scanf("%d%d%d",&w,&u,&v);
    			if(w==0){
    				printf("%lld
    ",query(1,1,n,u,v));
    			}
    			else if(w==1){
    				insert(1,1,n,u,u,v);
    			}
    		}
    	}
    	return 0;
    }
    

    T3:Transformation

    (Description:)

    一个长度为(n(n leq 10^5))的序列,初始值全为零
    维护区间的和,平方和,立方和
    支持区间加法,区间乘法

    (Solution:)

    区间和很好维护,关键在于区间的平方和和立方和
    讨论区间([l,r]),令它们的和,平方和,立方和分别为(S_1)(S_2)(S_3)
    显然,对于区间乘法(S_iRightarrow S_i imes c^i)(c)为乘上的数,(iin (1,2,3))
    下面讨论区间加法,令加数为c:
    (S_1Rightarrow S_1+(r-l+1) imes c)
    (S_2Rightarrow S_2+2cS_1+(r-l+1) imes c^2)
    (S_3Rightarrow S_3+3cS_2+3c^2S_1+(r-l+1) imes c^3)
    由上述公式可以直接对三种和进行维护

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) (x<<1)
    #define rr(x) (x<<1|1)
    #define mod (10007)
    using namespace std;
    typedef long long lol;
    int n,m;
    lol Pow(lol x,int y){
    	lol ans=1;
    	x%=mod;
    	while(y){
    		if(y&1)ans=(ans*x)%mod;
    		x=(x*x)%mod;
    		y>>=1;
    	}
    	return ans;
    }
    struct segment{
    	lol sum[4],lazy_add,lazy_mul,lazy_same;
    	void clear(){
    		for(int i=1;i<=3;i++)
    			sum[i]=0;
    		lazy_add=0;
    		lazy_same=-1;
    		lazy_mul=1;
    	}
    	void add(int l,int r,lol w){
    		int len=(r-l+1)%mod;
    		(sum[3]+=3*w%mod*sum[2]%mod+3*Pow(w,2)%mod*sum[1]%mod+Pow(w,3)*len%mod)%=mod;
    		(sum[2]+=2*w%mod*sum[1]%mod+Pow(w,2)*len%mod)%mod;
    		(sum[1]+=w*len%mod)%=mod;	
    	}
    }sgm[400005];
    void push_up(int root){
    	for(int i=1;i<=3;i++)
    		sgm[root].sum[i]=(sgm[ll(root)].sum[i]+sgm[rr(root)].sum[i])%mod;
    }
    void push_down(int root,int left,int right){
    	if(left>right)return;
    	int mid=(left+right)>>1;
    	if(sgm[root].lazy_same!=-1){
    		lol w=sgm[root].lazy_same%mod;
    		for(int i=1;i<=3;i++){
    			sgm[ll(root)].sum[i]=(mid-left+1)%mod*Pow(w,i)%mod;
    			sgm[rr(root)].sum[i]=(right-mid)%mod*Pow(w,i)%mod;
    		}
    		sgm[ll(root)].lazy_add=sgm[rr(root)].lazy_add=0;
    		sgm[ll(root)].lazy_mul=sgm[rr(root)].lazy_mul=1;
    		sgm[ll(root)].lazy_same=sgm[rr(root)].lazy_same=w;
    		sgm[root].lazy_same=-1;
    	}
    	if(sgm[root].lazy_mul!=1){
    		lol w=sgm[root].lazy_mul%mod;
    		for(int i=1;i<=3;i++){
    			(sgm[ll(root)].sum[i]*=Pow(w,i))%=mod;
    			(sgm[rr(root)].sum[i]*=Pow(w,i))%=mod;
    		}
    		if(sgm[ll(root)].lazy_same!=-1)(sgm[ll(root)].lazy_same*=w)%=mod;
    		else{
    			(sgm[ll(root)].lazy_add*=w)%=mod;
    			(sgm[ll(root)].lazy_mul*=w)%=mod;
    		}
    		if(sgm[rr(root)].lazy_same!=-1)(sgm[rr(root)].lazy_same*=w)%=mod;
    		else{
    			(sgm[rr(root)].lazy_add*=w)%=mod;
    			(sgm[rr(root)].lazy_mul*=w)%=mod;
    		}
    		sgm[root].lazy_mul=1;
    	}
    	if(sgm[root].lazy_add){
    		lol w=sgm[root].lazy_add%mod;
    		sgm[ll(root)].add(left,mid,w);
    		sgm[rr(root)].add(mid+1,right,w);
    		if(sgm[ll(root)].lazy_same!=-1)(sgm[ll(root)].lazy_same+=w)%=mod;
    		else{(sgm[ll(root)].lazy_add+=w)%=mod;}
    		if(sgm[rr(root)].lazy_same!=-1)(sgm[rr(root)].lazy_same+=w)%=mod;
    		else{(sgm[rr(root)].lazy_add+=w)%=mod;}
    		sgm[root].lazy_add=0;
    	}
    }
    void insert(int root,int left,int right,int l,int r,int kind,lol w){
    	if(l<=left&&right<=r){
    		if(kind==1){ 
    			if(sgm[root].lazy_same!=-1)(sgm[root].lazy_same+=w)%=mod;
    			else{(sgm[root].lazy_add+=w)%=mod;} 
    			sgm[root].add(left,right,w);
    		}
    		else if(kind==2){
    			if(sgm[root].lazy_same!=-1)(sgm[root].lazy_same*=w)%=mod;
    			else{
    				(sgm[root].lazy_add*=w)%=mod;
    				(sgm[root].lazy_mul*=w)%=mod;
    			}
    			for(int i=1;i<=3;i++)
    				(sgm[root].sum[i]*=Pow(w,i))%=mod;
    		}
    		else if(kind==3){
    			sgm[root].lazy_add=0;
    			sgm[root].lazy_mul=1;
    			sgm[root].lazy_same=w;
    			for(int i=1;i<=3;i++)
    				sgm[root].sum[i]=(right-left+1)%mod*Pow(w,i)%mod;
    		}
    		return;
    	}
    	if(left>r||l>right)return;
    	push_down(root,left,right);
    	int mid=(left+right)>>1;
    	insert(ll(root),left,mid,l,r,kind,w);
    	insert(rr(root),mid+1,right,l,r,kind,w);
    	push_up(root);
    }
    lol query(int root,int left,int right,int l,int r,int t){
    	if(l<=left&&right<=r)return sgm[root].sum[t]%mod;
    	if(left>r||l>right)return 0;
    	push_down(root,left,right);
    	int mid=(left+right)>>1;
    	return (query(ll(root),left,mid,l,r,t)+query(rr(root),mid+1,right,l,r,t))%mod;
    }
    int main(){
    	freopen("Cin.txt","r",stdin);
    	while(1){
    		scanf("%d%d",&n,&m);
    		if(n==0&&m==0)break;
    		for(int i=1;i<=n*4;i++)
    			sgm[i].clear();
    		for(int i=1;i<=m;i++){
    			int k,u,v;
    			lol w;
    			scanf("%d%d%d%lld",&k,&u,&v,&w);
    			w%=mod;
    			if(k==4)printf("%lld
    ",query(1,1,n,u,v,w));
    			else{insert(1,1,n,u,v,k,w);} 
    		}
    	}
    	return 0;
    }
    

    T4:Legacy

    (Description:)

    一张有(n(n leq 10^5))个点的图,由(q(q leq 10^5))个操作,类型如下:
    1.从(u)(v)连一条长度为w的边
    2.从(u)([l,r])区间内的每一点都连一条长度为(w)的边
    3.从([l,r])区间内的每一点到(v)都连一条长度为(w)的边
    求从(s)到每个节点的最短距离,不能到达则为(-1)

    (Solution:)

    显然,如果图建完了,就只要跑一遍最短路就完事,但是事实不是这样啊
    考虑到这有可能是一张完全图,所以如果真的每一条边老老实实建,时间空间就...起飞~
    由于每次连边的是相邻的一个区间,我们考虑线段树优化建图:
    建立线段树(A),每一个子节点向父节点连边,边权为(0)
    建立线段树(B),每一个父节点向子节点连边,边权为(0)
    (B)的每一个叶子节点向(A)的对应叶子节点连边,边权为(0)
    对于操作(1),我们将(A)的第(u)个叶子节点向(B)的第(v)个叶子节点连一条权值为(w)的边
    对于操作(2),我们将(A)的第(u)个叶子节点向(B)([l,r])区间节点连一条权值为(w)的边
    对于造作(3),我们将(A)([l,r])区间节点向(B)的第(v)个叶子节点连一条权值为(w)的边
    这样每次的建边数量为(log_2^2 n)条,时间空间效率就有了保证
    最后的结果就是(A)的第(s)个叶子节点到其他叶子节点的最短路

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) (x<<1)
    #define rr(x) (x<<1|1)
    using namespace std;
    typedef long long lol;
    const int N=100005;
    const lol inf=1e18+5;
    int tot,n,m,l,a[N],b[N];
    struct node{
    	int to;
    	lol dis;
    	node(int _to,lol _dis){to=_to;dis=_dis;}
    };
    vector<node>edge[N<<3];
    struct segment{
    	int num;
    }sgm_a[N<<2],sgm_b[N<<2];
    void build_a(int root,int left,int right){
    	sgm_a[root].num=++tot;
    	if(left==right){
    		a[left]=sgm_a[root].num;
    		return;
    	}
    	if(left>right)return;
    	int mid=(left+right)>>1;
    	build_a(ll(root),left,mid);
    	build_a(rr(root),mid+1,right);
    	edge[sgm_a[ll(root)].num].push_back(node(sgm_a[root].num,0ll));
    	edge[sgm_a[rr(root)].num].push_back(node(sgm_a[root].num,0ll));
    }
    void insert_a(int root,int left,int right,int l,int r,int s,lol dis){
    	if(l<=left&&right<=r){
    		edge[sgm_a[root].num].push_back(node(b[s],dis));
    		return;
    	}
    	if(l>right||left>r)return;
    	int mid=(left+right)>>1;
    	insert_a(ll(root),left,mid,l,r,s,dis);
    	insert_a(rr(root),mid+1,right,l,r,s,dis);
    }
    void build_b(int root,int left,int right){
    	sgm_b[root].num=++tot;
    	if(left==right){
    		b[left]=sgm_b[root].num;
    		return;
    	}
    	if(left>right)return;
    	int mid=(left+right)>>1;
    	build_b(ll(root),left,mid);
    	build_b(rr(root),mid+1,right);
    	edge[sgm_b[root].num].push_back(node(sgm_b[ll(root)].num,0ll));
    	edge[sgm_b[root].num].push_back(node(sgm_b[rr(root)].num,0ll));
    }
    void insert_b(int root,int left,int right,int l,int r,int s,lol dis){
    	if(l<=left&&right<=r){
    		edge[a[s]].push_back(node(sgm_b[root].num,dis));
    		return;
    	}
    	if(l>right||left>r)return;
    	int mid=(left+right)>>1;
    	insert_b(ll(root),left,mid,l,r,s,dis);
    	insert_b(rr(root),mid+1,right,l,r,s,dis);
    }
    int vis[N<<3];
    lol dis[N<<3];
    void dijkstra(int r){
    	memset(vis,0,sizeof(vis));
    	for(int i=1;i<=(n<<3);i++)dis[i]=inf;
    	dis[r]=0;
    	pair<lol,int>tmp;
    	priority_queue<pair<lol,int>,vector<pair<lol,int>>,greater<pair<lol,int>>>q;
    	q.push(pair<lol,int>(0ll,r));
    	while(!q.empty()){
    		tmp=q.top();q.pop();
    		int u=tmp.second;
    		if(tmp.first>dis[u])continue;
    		vis[u]=1;
    		for(int i=0;i<edge[u].size();i++){
    			int v=edge[u][i].to;
    			if(vis[v])continue;
    			if(dis[v]>dis[u]+edge[u][i].dis){
    				dis[v]=dis[u]+edge[u][i].dis;
    				q.push(pair<lol,int>(dis[v],v));
    			}
    		}
    	}
    } 
    int main(){
    	scanf("%d%d%d",&n,&m,&l);
    	build_a(1,1,n);
    	build_b(1,1,n);
    	for(int i=1;i<=n;i++)edge[b[i]].push_back(node(a[i],0ll));
    	while(m--){
    		int k,u,v,s;
    		lol w;
    		scanf("%d",&k);
    		if(k==1){
    			scanf("%d%d%lld",&u,&v,&w);
    			edge[a[u]].push_back(node(b[v],w));
    		}
    		else if(k==2){
    			scanf("%d%d%d%lld",&s,&u,&v,&w);
    			insert_b(1,1,n,u,v,s,w);
    		}
    		else if(k==3){
    			scanf("%d%d%d%lld",&s,&u,&v,&w);
    			insert_a(1,1,n,u,v,s,w);
    		}
    	}
    	dijkstra(a[l]);
    	for(int i=1;i<=n;i++){
    		if(dis[a[i]]!=inf){
    			printf("%lld ",dis[a[i]]);
    		}
    		else printf("-1 ");
    	}
    	return 0;
    }
    

    T5:Till I Collapse

    (Description:)

    (n)个数划分成(m)段使得每中不同数字的个数(le k). 对于每个(k)满足(1le kle n)求出最小的(m)

    (Solution:)

    主席树裸题,然而本蒟蒻并没有想出来,原谅我学艺不精
    将原序列反过来遍历,建立主席树,每次将当前数上次出现的位置(-1),将这次出现的位置(+1)
    这样建出来的主席树,第(i)棵树的每一个区间的值代表这个区间有多少个数是(isim n)中第一个出现的
    (卧槽,真的神奇,蒟蒻由衷地赞叹)
    然后就是一个贪心了,从当前数开始向后尽可能的扩展区间
    直到不能再扩展,也就是区间内会出现第(k+1)个数的时候就从下一位开始另辟一个区间
    这样的能保证(m)是最小的

    (Code:)

    #include<iostream>
    #define ll(x) seg[x].l
    #define rr(x) seg[x].r
    using namespace std;
    const int N=100005;
    int n,tot,root[N],a[N],last[N];
    struct Segment{
    	int l,r,sum;
    }seg[N*40];
    void insert(int pre,int &now,int left,int right,int x,int w){
    	now=++tot;
    	seg[now]=seg[pre];
    	seg[now].sum+=w;
    	if(left==right)return; 
    	int mid=(left+right)>>1;
    	if(x<=mid)insert(ll(pre),ll(now),left,mid,x,w);
    	else insert(rr(pre),rr(now),mid+1,right,x,w);
    }
    int query(int now,int left,int right,int k){
    	if(left==right){
    		if(seg[now].sum<=k)return left;
    		else return left-1;
    	}
    	int mid=(left+right)>>1;
    	if(seg[ll(now)].sum<=k)return query(rr(now),mid+1,right,k-seg[ll(now)].sum);
    	else return query(ll(now),left,mid,k);
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	for(int i=n;i>=1;i--){
    		if(last[a[i]]){
    			insert(root[i+1],root[i],1,n,last[a[i]],-1);
    			insert(root[i],root[i],1,n,i,1);
    		}
    		else insert(root[i+1],root[i],1,n,i,1);
    		last[a[i]]=i;
    	}
    	for(int i=1;i<=n;i++){
    		int ans=0,u=1;
    		while(u<=n){
    			int v=query(root[u],1,n,i);
    			u=v+1;
    			ans++;
    		}
    		printf("%d ",ans);
    	}
    	puts("");
    }
    

    T6:Kth number

    (Description:)

    还用说吗哈哈哈

    (Solution:)

    (cdot)主席树裸题(其实很早就写过了)
    顺便学习了一下如何优雅地离散化(doge)

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) seg[x].l
    #define rr(x) seg[x].r
    using namespace std;
    const int N=100005;
    struct Segment{
    	int l,r,sum;
    }seg[N*40];
    int n,m,t,b[N],a[N],lsh[N],tot,root[N];
    void insert(int pre,int &now,int left,int right,int x,int w){
    	now=++tot;
    	seg[now]=seg[pre];
    	seg[now].sum+=w;
    	if(left==right)return;
    	int mid=(left+right)>>1;
    	if(x<=mid)insert(ll(pre),ll(now),left,mid,x,w);
    	else insert(rr(pre),rr(now),mid+1,right,x,w);
    }
    int query(int r1,int r2,int left,int right,int k){
    	if(left==right)return left;
    	int mid=(left+right)>>1;
    	if(seg[ll(r2)].sum-seg[ll(r1)].sum>=k)query(ll(r1),ll(r2),left,mid,k);
    	else query(rr(r1),rr(r2),mid+1,right,k-(seg[ll(r2)].sum-seg[ll(r1)].sum));
    }
    int main(){
    	scanf("%d",&t);
    	while(t--){
    		tot=0;
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;i++){
    			scanf("%d",&a[i]);
    			lsh[i]=a[i];
    		}
    		sort(lsh+1,lsh+n+1);
    		int cnt=unique(lsh+1,lsh+n+1)-lsh-1;
    		for(int i=1;i<=n;i++){
    			b[i]=lower_bound(lsh+1,lsh+cnt+1,a[i])-lsh;
    			insert(root[i-1],root[i],1,n,b[i],1);
    		}
    		while(m--){
    			int u,v,k;
    			scanf("%d%d%d",&u,&v,&k);
    			printf("%d
    ",lsh[query(root[u-1],root[v],1,n,k)]);
    		}
    	}
    	return 0;
    }
    

    T7:To the moon

    (Description:)

    维护一个序列的区间和,历史区间和,支持区间修改和时间回溯

    (Solution:)

    主席树模板题,只不过是区间修改和查询
    由于主席树不能直接(pushdown),因为某些子节点是上一个时间戳的,所以本题的查询操作都是用递归的过程中记录(lazy)的和来实现的
    对于时间回溯,就直接给(t)赋个值,然后更新一下下一次新节点的生成位置来节省空间即可

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) seg[x].l
    #define rr(x) seg[x].r
    using namespace std;
    typedef long long lol;
    const int N=100005;
    struct Segment{
    	int l,r;
    	lol sum,lazy;
    }seg[N*40];
    int n,m,t,root[N],tot;
    lol a[N];
    void push_up(int now,int left,int right){
    	seg[now].sum=seg[ll(now)].sum+seg[rr(now)].sum+seg[now].lazy*(right-left+1);
    }
    void build(int &now,int left,int right){
    	if(left>right){now=0;return;}
    	now=++tot;
    	seg[now].lazy=0;
    	if(left==right){
    		seg[now].sum=a[left];
    		ll(now)=rr(now)=0;
    		return;
    	}
    	int mid=(left+right)>>1;
    	build(ll(now),left,mid);
    	build(rr(now),mid+1,right);
    	push_up(now,left,right);
    }
    void insert(int pre,int &now,int left,int right,int l,int r,lol w){
    	if(left>r||l>right)return;
    	now=++tot;
    	seg[now]=seg[pre];
    	if(l<=left&&right<=r){
    		seg[now].sum+=(right-left+1)*w;
    		seg[now].lazy+=w;
    		return;
    	}
    	int mid=(left+right)>>1;
    	insert(ll(pre),ll(now),left,mid,l,r,w);
    	insert(rr(pre),rr(now),mid+1,right,l,r,w);
    	push_up(now,left,right);
    }
    lol query(int now,int left,int right,int l,int r,lol w){
    	if(l<=left&&right<=r)return seg[now].sum+(right-left+1)*w;
    	if(l>right||left>r)return 0;
    	w+=seg[now].lazy;
    	int mid=(left+right)>>1;
    	return query(ll(now),left,mid,l,r,w)+query(rr(now),mid+1,right,l,r,w);
    }
    int main(){
    	while(scanf("%d%d",&n,&m)!=EOF){
    		for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    		tot=0;
    		t=0;
    		build(root[0],1,n);
    		while(m--){
    			char ch[2];
    			int u,v,h;
    			lol w;
    			scanf("%s",ch);
    			if(ch[0]=='C'){
    				scanf("%d%d%lld",&u,&v,&w);
    				t++;
    				insert(root[t-1],root[t],1,n,u,v,w);
    			}
    			else if(ch[0]=='Q'){
    				scanf("%d%d",&u,&v);
    				printf("%lld
    ",query(root[t],1,n,u,v,0));
    			}
    			else if(ch[0]=='H'){
    				scanf("%d%d%d",&u,&v,&h);
    				printf("%lld
    ",query(root[h],1,n,u,v,0));
    			}
    			else if(ch[0]=='B'){
    				int tt;
    				scanf("%d",&tt);
    				if(t!=tt){
    					t=tt;
    					tot=root[t+1]-1;
    				}
    			}
    		}
    	}
    	return 0;
    }
    

    T8:Just (h)-index

    (Description:)

    长度为(n(nle 10^5))的序列,(q(qle 10^5))个询问,每次询问一个区间中最大的(h),使得:
    当前区间中至少有(h)个数(ge h)

    (Solution:)

    要找(h),那当然就是二分查找咯
    维护一个值域主席树,对于每一次二分出来的(h),计算当前区间有多少个数是(ge h)的即可

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) seg[x].l
    #define rr(x) seg[x].r
    using namespace std;
    const int N=100005;
    int n,m,a[N],tot,root[N];
    struct Segment{
    	int l,r,sum;
    }seg[N*40];
    void push_up(int now){
    	seg[now].sum=seg[ll(now)].sum+seg[rr(now)].sum;
    }
    void insert(int pre,int &now,int left,int right,int l,int r,int w){
    	if(l>right||left>r)return;
    	now=++tot;
    	seg[now]=seg[pre];
    	if(l<=left&&right<=r){
    		seg[now].sum+=w*(right-left+1);
    		return;
    	}
    	int mid=(left+right)>>1;
    	insert(ll(pre),ll(now),left,mid,l,r,w);
    	insert(rr(pre),rr(now),mid+1,right,l,r,w);
    	push_up(now);
    }
    int query(int r1,int r2,int left,int right,int l,int r){
    	if(l<=left&&right<=r)return seg[r1].sum-seg[r2].sum;
    	if(l>right||left>r)return 0;
    	int mid=(left+right)>>1;
    	return query(ll(r1),ll(r2),left,mid,l,r)+query(rr(r1),rr(r2),mid+1,right,l,r);
    }
    int main(){
    	while(scanf("%d%d",&n,&m)!=EOF){
    		tot=0;
    		for(int i=1;i<=n;i++){
    			scanf("%d",&a[i]);
    			insert(root[i-1],root[i],1,n,a[i],a[i],1);
    		}
    		while(m--){
    			int u,v,l,r,ans=0;
    			scanf("%d%d",&u,&v);
    			l=0;r=v-u+1;
    			while(l<=r){
    				int mid=(l+r)>>1;
    				if(query(root[v],root[u-1],1,n,mid,n)>=mid){ans=mid;l=mid+1;}
    				else {r=mid-1;}
    			}
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    

    T9:Sequence II

    (Description:)

    给一个数列,有(m)个询问,每次问([l,r])区间中所有数在当前区间中第一次出现的位置的中位数是多少,强制在线

    (Solution:)

    (英文题面最怕的就是这种绕来绕去的题意,比赛真的容易翻车)
    倒序建主席树(这次本蒟蒻也觉得这是裸题了)
    然后对于每一次询问,先找到区间中有多少个不同的数(d)
    然后再用(frac{d+1}{2})代进去查找中位数即可

    (Code:)

    #include<bits/stdc++.h>
    #define ll(x) seg[x].l
    #define rr(x) seg[x].r
    using namespace std;
    const int N=200005;
    struct Segment{
    	int l,r,sum;
    }seg[N*40];
    int n,m,a[N],root[N],tot,last[N];
    void insert(int pre,int &now,int left,int right,int x,int w){
    	now=++tot;
    	seg[now]=seg[pre];
    	seg[now].sum+=w;
    	if(left==right)return;
    	int mid=(left+right)>>1;
    	if(x<=mid)insert(ll(pre),ll(now),left,mid,x,w);
    	else insert(rr(pre),rr(now),mid+1,right,x,w);
    }
    int query(int now,int left,int right,int l,int r){
    	if(l<=left&&right<=r)return seg[now].sum;
    	if(left>r||l>right)return 0;
    	int mid=(left+right)>>1;
    	return query(ll(now),left,mid,l,r)+query(rr(now),mid+1,right,l,r);
    }
    int find(int now,int left,int right,int k){
    	if(left==right)return left;
    	int mid=(left+right)>>1;
    	if(seg[ll(now)].sum>=k)return find(ll(now),left,mid,k);
    	else return find(rr(now),mid+1,right,k-seg[ll(now)].sum);
    }
    int main(){
    	int T;
    	scanf("%d",&T);
    	for(int t=1;t<=T;t++){
    		scanf("%d%d",&n,&m);
    		memset(last,0,sizeof(last));
    		memset(root,0,sizeof(root));
    		tot=0;
    		for(int i=1;i<=n;i++)
    			scanf("%d",&a[i]);
    		for(int i=n;i>=1;i--){
    			if(last[a[i]]){
    				insert(root[i+1],root[i],1,n,last[a[i]],-1);
    				insert(root[i],root[i],1,n,i,1);
    			}
    			else insert(root[i+1],root[i],1,n,i,1);
    			last[a[i]]=i;
    		}
    		int ans=0;
    		printf("Case #%d:",t);
    		while(m--){
    			int u,v;
    			scanf("%d%d",&u,&v);
    			u=(u+ans)%n+1;
    			v=(v+ans)%n+1;
    			if(u>v)swap(u,v);
    			int tmp=query(root[u],1,n,u,v);
    			ans=find(root[u],1,n,(tmp+1)>>1);
    			printf(" %d",ans);
    		}
    		printf("
    ");
    	}
    	return 0;
    }
    

    T10:Dynamic Rankings

    (咕~,树套树是真的没理解。。。)

  • 相关阅读:
    Android 编程下的计时器
    Android 编程下 java.lang.NoClassDefFoundError: cn.jpush.android.api.JPushInterface 报错
    Android 编程下的 TraceView 简介及其案例实战
    Android 编程下的日志工具类
    C#设计模式--装饰器模式
    C#设计模式--设配器模式
    C#设计模式--原型模式
    C#设计模式--建造者模式
    C#设计模式--单例模式
    C#设计模式--抽象工厂模式
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/13377891.html
Copyright © 2020-2023  润新知