[线段树&主席树]の一些题解
[线段树&主席树]の一些题解
T1:The Child and Sequence
\(Description:\)
给你一个长度为\(n(n \leq 10^5)\)的序列,要求支持区间求和,区间取模,单点修改
\(Solution:\)
区间求和和单点修改都是基本的线段树操作,关键在于区间取模。
这里我们维护一个区间最大值,这样如果模数大于当前区间的最大值,就不需要再往下走了
由于\(x \% y \leq\frac{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\n",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\n",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_i\Rightarrow S_i\times c^i\)(\(c\)为乘上的数,\(i\in (1,2,3)\))
下面讨论区间加法,令加数为c:
\(S_1\Rightarrow S_1+(r-l+1)\times c\)
\(S_2\Rightarrow S_2+2cS_1+(r-l+1)\times c^2\)
\(S_3\Rightarrow S_3+3cS_2+3c^2S_1+(r-l+1)\times 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\n",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\)满足\(1\le k\le n\)求出最小的\(m\)
\(Solution:\)
主席树裸题,然而本蒟蒻并没有想出来,原谅我学艺不精
将原序列反过来遍历,建立主席树,每次将当前数上次出现的位置\(-1\),将这次出现的位置\(+1\)
这样建出来的主席树,第\(i\)棵树的每一个区间的值代表这个区间有多少个数是\(i\sim 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\n",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\n",query(root[t],1,n,u,v,0));
}
else if(ch[0]=='H'){
scanf("%d%d%d",&u,&v,&h);
printf("%lld\n",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(n\le 10^5)\)的序列,\(q(q\le 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\n",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("\n");
}
return 0;
}
T10:Dynamic Rankings
(咕~,树套树是真的没理解。。。)