杂数据结构选做
持续更新ing...
更新多少取决于我卷了多少
似乎都是比较基础的东西,但是我数据结构太菜了,没办法
╮(╯_╰)╭
#9016. CodeChef MINXORSEG
有两种做法,我敲的后一种
第一种
先不考虑范围问题,考虑现在有三个点\(u,v,w\),若它们的\(lcp\)为\(l\),那么考虑第\(l+1\)位,根据鸽巢原理,必有两个点的第\(l+1\)位相等,设为\(u\)和\(v\),那么显然选这两个点的配对比选\((u/v,w)\)的配对更优
根据这点,我们考虑建\(trie\),然后将每个点都挂到它在\(trie\)树上经过的点上,因为树高为\(\log W\),所以共会挂\(N\log W\)个点,而\(trie\)树上的每个点中挂着的所有点组成的点对都可以成为上文的\((u,v)\)
但是显然不能暴力的找所有点对,所以把挂着的点按编号排序后,相邻两点组成一个点对即可,这样显然最优
那么我们现在就有\(N\log W\)个点对
再来考虑范围限制\([l,r]\),对于点对\((u,v)\),其合法当且仅当\(u,v\in[l,r]\),那么若我们把所有点对放到二维平面上,这就是一个二维空间数点问题,扫描线+树状数组即可
第二种
这种应该要比上一种好写一些,但是其实都挺好写的
其实两种做法差不多?
还是根据第一种中提到的,考虑\(lcp\),又结合第一种中提到的贪心,发现其实可以不用把所有点对都这样提出来
考虑直接贪心的对每个点\(x\)找到\(lcp\geq i\)的\(y\)(\(y<x\)),然后查询答案时也是扫描线+树状数组维护即可
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,q,a[N],p[N];
int b,to[N][30];
bool cmp(int x,int y){
if((a[x]>>b)==(a[y]>>b)) return x<y;
return (a[x]>>b)<(a[y]>>b);
}
struct node{
int l,r,id;
bool operator < (const node &other)const{
return r<other.r;
}
}cn[N];
int c[N],ans[N];
void add(int x,int y){ for(x=n-x+1;x<=n;x+=x&-x) c[x]=min(c[x],y); }
int ask(int x){
int ans=1e9;
for(x=n-x+1;x;x-=x&-x) ans=min(ans,c[x]);
return ans;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),p[i]=i,c[i]=1e9;
for(b=0;b<30;++b){
stable_sort(p+1,p+n+1,cmp);
for(int i=2;i<=n;++i) if((a[p[i]]>>b)==(a[p[i-1]])>>b) to[p[i]][b]=p[i-1];
}
for(int i=1;i<=q;++i) scanf("%d%d",&cn[i].l,&cn[i].r),cn[i].id=i;
sort(cn+1,cn+q+1);
for(int i=1,k=1;i<=n;++i){
for(int j=0;j<30;++j) if(to[i][j]) add(to[i][j],a[i]^a[to[i][j]]);
while(k<=q&&cn[k].r==i) ans[cn[k].id]=ask(cn[k].l),++k;
}
for(int i=1;i<=q;++i) printf("%d\n",ans[i]);
return 0;
}
P8078 [WC2022] 秃子酋长
从\(luogu\)的\(tj\)区偷的 如果在分治结构上很难快速合并某些信息,我们就可以利用分块来做所以如果在分治结构上很难快速合并某些信息,我们就可以利用分块来做。反过来,我们如果想要用任何的分治结构做这道题,我们都需要发现一个性质,支持某种程度上比暴力更快地合并区间信息的性质。
考虑如果我们知道了\([l,mid]\)的答案以及\([mid+1,r]\)的答案,能否合并
将\([l,mid]\)中以及\([mid+1,r]\)的数按权值从小到大排好序,考虑\(a_i\)后面是\(a_j\),那么记录\(ri_i=j\),\(le_j=i\)
若\(i,j\in[l,mid]\),那么若\([mid+1,r]\)中存在\(k\)使得\(a_k\in[a_i,a_j]\),也即\(a_k\)最终会插入\(a_i,a_j\)中间,那么手摸一下可以发现,代价从\(|i-j|\)变成了\(k-i+k-j\),也即增加了\(2k-2\max(i,j)\),若插入的不止有\(k\),依旧还是有变化的权值中与\(i,j\)有关的部分为\(-2\max(i,j)\)
若\(i\in[l,mid]\)且\(a_i\)是这段区间内最大的那个,如果\([mid+1,r]\)中存在\(k\)使得\(a_k>a_i\)也即\(a_k\)最终会放到\(a_i\)的右边,可以发现贡献最终增加了\(k-i\),若有多个放在\(a_i\)的右边,依旧可以发现变化的权值中与\(i\)相关的值依旧是\(-i\)
对于\(a_i\)是最小的那个同理,贡献和第二种情况相同
当\(i,j\in[mid+1,r]\)时,第一种情况对应的贡献为\(2min(i,j)\),第二种和第三种为\(i\)
那么就可以猫树分治\(O(N\log^2N)\)的做了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+5;
int n,m,a[N],p[N];
struct node{ int l,r,id; };
bool cmp1(node a,node b){ return a.l<b.l; }
bool cmp2(node a,node b){ return a.r>b.r; }
ll ans[N];
vector<node> cn;
ll suf[N],pre[N],c[N];
queue<int> op;
void add(int x,int y){ if(abs(x)>N) return ; (x<0)&&(x+=n+1),op.push(x); for(;x<=n;x+=x&-x) c[x]+=y; }
ll ask(int x){
ll ans=0;
for((x<0)&&(x+=n+1);x;x-=x&-x) ans+=c[x];
return ans;
}
void clear(){
while(!op.empty()){
int x=op.front(); op.pop();
for(;x<=n;x+=x&-x) c[x]=0;
}
}
int le[N],ri[N],pos[N],A,B;
void work(int tl,int tr,int &A,int k){
for(int x=tl,y=tr;x;x=ri[x]){
while(y&&a[y]<a[x]) (x==tl)&&(A=min(A,k*y)),y=ri[y];
while(y&&a[x]<a[y]&&(!ri[x]||a[y]<a[ri[x]])) pos[x]=min(pos[x],k*y),y=ri[y];
}
}
void Val(int now,int &A,int k,int k1=1,bool flag1=1,bool flag2=1){
if(flag1){
if(le[now]) add(-k*pos[le[now]],2*k*k1*abs(max(-k*le[now],-k*now))); else add(-k*A,k*k1*now);
}
if(flag2){
if(ri[now]) add(-k*pos[now],2*k*k1*abs(max(-k*ri[now],-k*now))); else add(-k*pos[now],k*k1*now);
}
}
void work1(int now,int &A,int k){
Val(now,A,k,-1);
if(le[now]) ri[le[now]]=ri[now],(pos[le[now]]==1e9)?(pos[le[now]]=pos[now]):((pos[now]!=1e9)&&(pos[le[now]]=abs(min(-k*pos[le[now]],-k*pos[now])))); else (A==1e9)?(A=pos[now]):((pos[now]!=1e9)&&(A=abs(min(-k*A,-k*pos[now]))));
if(ri[now]) le[ri[now]]=le[now];
if(le[now]) Val(le[now],A,k,1,0,1);
else if(ri[now]) Val(ri[now],A,k,1,1,0);
}
void solve(int l,int r,vector<node> que,bool flag){
if(l==r) return ;
int mid=l+r>>1; vector<node> q1,q2,q3;
for(auto x:que){
if(x.r<=mid) q1.push_back(x);
else if(x.l>mid) q2.push_back(x);
else q3.push_back(x);
}
solve(l,mid,q1,0),solve(mid+1,r,q2,1);
for(auto x:q3) ans[x.id]=suf[x.l]+pre[x.r];
set<int> s; A=B=1e9;
for(int i=mid;i>=l;--i) s.insert(a[i]),pos[i]=1e9,le[i]=ri[i]=0;
int las=0,stl=0,str=0; for(auto x:s) le[p[x]]=p[las],(las)&&(ri[p[las]]=p[x]),las=x,(!stl)&&(stl=p[x]);
s.clear(); for(int i=mid+1;i<=r;++i) s.insert(a[i]),pos[i]=1e9,le[i]=ri[i]=0;
las=0; for(auto x:s) le[p[x]]=p[las],(las)&&(ri[p[las]]=p[x]),las=x,(!str)&&(str=p[x]);
work(stl,str,A,1),work(str,stl,B,-1),sort(q3.begin(),q3.end(),cmp1);
if(B<0) B=-B;
for(int i=mid+1;i<=r;++i) if(pos[i]<0) pos[i]=-pos[i];
clear(),add(A,-stl);
for(int now=l;now<=mid;++now){
if(ri[now]) add(pos[now],-2*max(ri[now],now)); else add(pos[now],-now);
}
for(int now=l,i=0;now<=mid;++now){
for(;i<q3.size()&&q3[i].l==now;++i) ans[q3[i].id]+=ask(q3[i].r);
work1(now,A,-1);
}
sort(q3.begin(),q3.end(),cmp2),clear();
add(-B,str);
for(int now=mid+1;now<=r;++now){
if(ri[now]) add(-pos[now],2*min(ri[now],now)); else add(-pos[now],now);
}
for(int now=r,i=0;now>mid;--now){
for(;i<q3.size()&&q3[i].r==now;++i) ans[q3[i].id]+=ask(-q3[i].l);
work1(now,B,1);
}
if(!flag){//suf
set<int> s;
for(int i=r;i>=l;--i){
suf[i]=i==r?0:suf[i+1];
auto it=s.lower_bound(a[i]);
if(it!=s.end()){
if(it!=s.begin()) suf[i]-=abs(p[*it]-p[*prev(it)]);
suf[i]+=p[*it]-i;
}
if(it!=s.begin()) suf[i]+=p[*prev(it)]-i;
s.insert(a[i]);
}
}else{//pre
set<int> s;
for(int i=l;i<=r;++i){
pre[i]=i==l?0:pre[i-1];
auto it=s.lower_bound(a[i]);
if(it!=s.end()){
if(it!=s.begin()) pre[i]-=abs(p[*it]-p[*prev(it)]);
pre[i]+=i-p[*it];
}
if(it!=s.begin()) pre[i]+=i-p[*prev(it)];
s.insert(a[i]);
}
}
}
int main(){
scanf("%d%d",&n,&m),cn.resize(m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),p[a[i]]=i;
for(int i=0;i<m;++i) scanf("%d%d",&cn[i].l,&cn[i].r),cn[i].id=i+1;
solve(1,n,cn,0);
for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}
标签:node,suf,选做,return,int,mid,数据结构,las
From: https://www.cnblogs.com/LuoyuSitfitw/p/18223390