8.数据结构
数据结构
开题顺序: \(WHABCEIG\)
\(A\) CF2042D Recommendations
-
扫描线维护 \(\le l\) 的最大值和 \(\ge r\) 的最小值。
点击查看代码
int l[200010],r[200010],d[200010],st[200010],ed[200010]; vector<pair<int,int> >q[200010]; multiset<int>s; multiset<int>::iterator it; void slove1(int n) { s.clear(); for(int i=1;i<=n;i++) { q[i].clear(); d[i]=r[i]; } sort(d+1,d+1+n); d[0]=unique(d+1,d+1+n)-(d+1); for(int i=1;i<=n;i++) q[lower_bound(d+1,d+1+d[0],r[i])-d].push_back(make_pair(l[i],i)); for(int i=d[0];i>=1;i--) { sort(q[i].begin(),q[i].end()); for(int j=0;j<q[i].size();j++) s.insert(q[i][j].first); for(int j=0;j<q[i].size();j++) { s.erase(s.find(q[i][j].first)); it=s.upper_bound(q[i][j].first); if(it!=s.begin()&&s.empty()==0) st[q[i][j].second]=*prev(it); s.insert(q[i][j].first); } } } void slove2(int n) { s.clear(); for(int i=1;i<=n;i++) { q[i].clear(); d[i]=l[i]; } sort(d+1,d+1+n); d[0]=unique(d+1,d+1+n)-(d+1); for(int i=1;i<=n;i++) q[lower_bound(d+1,d+1+d[0],l[i])-d].push_back(make_pair(r[i],i)); for(int i=1;i<=d[0];i++) { sort(q[i].begin(),q[i].end(),greater<pair<int,int> >()); for(int j=0;j<q[i].size();j++) s.insert(q[i][j].first); for(int j=0;j<q[i].size();j++) { s.erase(s.find(q[i][j].first)); it=s.lower_bound(q[i][j].first); if(it!=s.end()&&s.empty()==0) ed[q[i][j].second]=*it; s.insert(q[i][j].first); } } } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int t,n,i,j; cin>>t; for(j=1;j<=t;j++) { cin>>n; for(i=1;i<=n;i++) st[i]=ed[i]=0x3f3f3f3f; for(i=1;i<=n;i++) cin>>l[i]>>r[i]; slove1(n); slove2(n); for(i=1;i<=n;i++) cout<<(st[i]!=0x3f3f3f3f&&ed[i]!=0x3f3f3f3f)*((ed[i]-st[i]+1)-(r[i]-l[i]+1))<<endl; } return 0; }
\(B\) HDU5603 the soldier of love
-
正难则反,考虑统计不包含组中任意一个给定点的段数,划分成 \(k\) 段区间后扫描线维护。
点击查看代码
int l[300010],r[300010],x[300010],ans[300010]; vector<int>c[1000010]; vector<pair<int,int> >q[1000010]; struct BIT { int c[1000010]; void clear() { memset(c,0,sizeof(c)); } int lowbit(int x) { return (x&(-x)); } void add(int n,int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val; } int getsum(int x) { int ans=0; for(int i=x;i>=1;i-=lowbit(i)) ans+=c[i]; return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,k,i,j; while(scanf("%d%d",&n,&m)==2) { T.clear(); for(i=1;i<=n;i++) { scanf("%d%d",&l[i],&r[i]); c[l[i]].push_back(r[i]); } for(i=1;i<=m;i++) { scanf("%d",&k); ans[i]=0; x[0]=0; x[k+1]=1000001; for(j=1;j<=k;j++) scanf("%d",&x[j]); for(j=1;j<=k+1;j++) { if(x[j]-1>=x[j-1]+1) q[x[j-1]+1].push_back(make_pair(x[j]-1,i)); } } for(i=1000000;i>=1;i--) { for(j=0;j<c[i].size();j++) T.add(1000000,c[i][j],1); for(j=0;j<q[i].size();j++) ans[q[i][j].second]+=T.getsum(q[i][j].first); c[i].clear(); q[i].clear(); } for(i=1;i<=m;i++) printf("%d\n",n-ans[i]); } return 0; }
\(C\) UOJ 637. 【美团杯2021】A. 数据结构
-
正难则反。观察到 \(x\) 对答案产生贡献当且仅当所有的 \(x\) 都在 \([l,r]\) 内且 \([l,r]\) 中不含有 \(x-1\) 。不妨先假设所有出现过的元素都对所有区间产生 \(+1\) 的贡献,然后维护变化的贡献。
-
像上题一次性划分成若干段区间后难以进行数颜色,但可以借鉴划分区间的思想,在扫描线的过程中划分并更新答案。
-
设 \(a_{i}\) 的极长存在区间为 \([st_{a_{i}},ed_{a_{i}}]\) 。对右端点进行扫描线的过程中,考虑维护每个元素会对什么样的询问产生贡献。
-
针对第二个限制, \(a_{i}\) 会对询问左端点 \(l \in [last_{a_{i}}+1,\min(st_{a_{i}+1},i)]\) 的答案产生 \(+1\) 的贡献。
-
针对第一个限制,当 \(ed_{a_{i}}=i\) 时, \(a_{i}\) 无法对询问左端点 \(l \in [last_{a_{i}-1}+1,st_{a_{i}}]\) 的询问产生贡献,故减 \(1\) 。
点击查看代码
int a[1000010],st[1000010],ed[1000010],ans[1000010],last[1000010]; vector<pair<int,int> >q[1000010]; struct BIT { int c[1000010]; int lowbit(int x) { return (x&(-x)); } void add(int n,int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val; } void update(int n,int l,int r,int val) { add(n,l,val); add(n,r+1,-val); } int getsum(int x) { int ans=0; for(int i=x;i>=1;i-=lowbit(i)) ans+=c[i]; return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,sum=0,l,r,i,j; scanf("%d%d",&n,&m); fill(st+0,st+n+2,n+1); for(i=1;i<=n;i++) { scanf("%d",&a[i]); if(st[a[i]]==n+1) { st[a[i]]=i; sum++; } ed[a[i]]=i; } for(i=1;i<=m;i++) { cin>>l>>r; q[r].push_back(make_pair(l,i)); } for(i=1;i<=n;i++) { if(ed[a[i]]==i&&last[a[i]-1]+1<=st[a[i]]) T.update(n,last[a[i]-1]+1,st[a[i]],-1); if(ed[a[i]+1]<=i&&last[a[i]]+1<=min(st[a[i]+1],i)) T.update(n,last[a[i]]+1,min(st[a[i]+1],i),1); last[a[i]]=i; for(j=0;j<q[i].size();j++) ans[q[i][j].second]=T.getsum(q[i][j].first); } for(i=1;i<=m;i++) cout<<sum+ans[i]<<endl; return 0; }
\(D\) luogu P3348 [ZJOI2016] 大森林
\(E\) QOJ 8672. 排队
-
序列离散点值函数复合
- 插入-标记-回收 常用于解决函数复合问题,需要扫描线和支持全局进行函数操作的数据结构辅助维护。
- 分别将询问在左右端点各存储一次,然后从左往右进行扫描线。
- 插入
- 若当前扫到的 \(i\) 是某个询问的左端点 \(l\) ,则将其对应的 \(x\) 放入数据结构中。
- 标记
- 对数据结构进行 \(f_{i}\) 的操作。
- 回收
- 若当前扫到的 \(i\) 是某个询问的右端点 \(r\) ,则将其对应的 \(l\) 放入数据结构中的点的答案作为该询问的答案。
- 需要自顶向下下传所有标记。
- 为方便代码实现,记录 \(\{ it \}\) 表示第 \(i\) 个询问对应的节点,并记录父亲节点。
-
函数操作和 [ABC389F] Rated Range 一样做即可,常数较大,多交几发就过了。
点击查看代码
int l[1000010],r[1000010],ans[1000010],it[1000010]; pair<int,int>ql[1000010],qr[1000010]; struct BST { int root,rt_sum; struct FHQ_Treap { int son[2],fa,val,rnd,cnt,siz,lazy; }tree[1000010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define fa(rt) (tree[rt].fa) BST() { rt_sum=0; srand(time(0)); } int build_rt(int val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=fa(rt_sum)=tree[rt_sum].lazy=0; tree[rt_sum].val=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=1; return rt_sum; } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; fa(rt)=0; if(lson(rt)!=0) fa(lson(rt))=rt; if(rson(rt)!=0) fa(rson(rt))=rt; } void pushlazy(int rt,int lazy) { tree[rt].lazy+=lazy; tree[rt].val+=lazy; } void pushdown(int rt) { pushlazy(lson(rt),tree[rt].lazy); pushlazy(rson(rt),tree[rt].lazy); tree[rt].lazy=0; } void split(int rt,int val,int &x,int &y) { if(rt==0) { x=y=0; return; } pushdown(rt); if(tree[rt].val<=val) { x=rt; split(rson(rt),val,rson(x),y); } else { y=rt; split(lson(rt),val,x,lson(y)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; pushdown(rt1); pushdown(rt2); if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } int insert(int val) { int x,y; split(root,val,x,y); root=merge(merge(x,build_rt(val)),y); return rt_sum; } void update(int l,int r) { int x,y,rt; split(root,r,x,y); split(x,l-1,x,rt); pushlazy(rt,1); root=merge(merge(x,rt),y); } int query(int rt) { int ans=tree[rt].val; for(rt=fa(rt);rt!=0;rt=fa(rt)) ans+=tree[rt].lazy; return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,x,y,i,j,k; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d%d",&l[i],&r[i]); for(i=1;i<=m;i++) { scanf("%d%d",&x,&y); ql[i]=make_pair(x,i); qr[i]=make_pair(y,i); } sort(ql+1,ql+1+m); sort(qr+1,qr+1+m); for(i=j=k=1;i<=n;i++) { for(;ql[j].first==i;j++) it[ql[j].second]=T.insert(0); T.update(l[i],r[i]); for(;qr[k].first==i;k++) ans[qr[k].second]=T.query(it[qr[k].second]); } for(i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
\(F\) luogu P9999 [Ynoi2000] tmostnrq
\(G\) luogu P8264 [Ynoi Easy Round 2020] TEST_100
-
序列函数复合
- 因为有强制在线的限制,可能会有 \(O(n^{2}V)\) 种不同的询问,此时不能只维护少数点值,而需要维护所有点值的函数复合结果。
- 可以考虑对序列进行分治,在分治结构上维护区间函数复合后的结果。
- 对分治结构拆出的每个子问题,预处理全局函数复合后的结果。查询的时候按顺序将 \(x\) 变成 \(x\) 在这些子问题上的全局函数复合的结果。
-
本题中因 \(n,V\) 同阶,不妨考虑分块。设 \(f_{i,j}\) 表示 \(j\) 经过第 \(i\) 个块后的值。
-
预处理时需要 \(FHQ-Treap\) 有交合并。实际实现时可以不每次重构整棵树而是复制一个空版本或使用 OI-Wiki 中的方法。
点击查看代码
int a[100010],f[50][100010],L[100010],R[100010],pos[100010],it[100010],ans[100010],klen,ksum; mt19937 rng(random_device{}()); struct BST { int id[100010],root,rt_sum; struct FHQ_Treap { int son[2],fa,rnd,cnt,siz,val,mn,mx,add,rev; }tree[100010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define fa(rt) (tree[rt].fa) int find(int x) { return id[x]==x?x:id[x]=find(id[x]); } int build_rt(int val) { rt_sum++; int rt=rt_sum; lson(rt)=rson(rt)=fa(rt)=tree[rt].add=tree[rt].rev=0; tree[rt].val=tree[rt].mn=tree[rt].mx=val; tree[rt].rnd=rng(); tree[rt].cnt=tree[rt].siz=1; return id[rt]=rt; } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; fa(rt)=0; if(lson(rt)!=0) { tree[rt].mn=tree[lson(rt)].mn; fa(lson(rt))=rt; } else tree[rt].mn=tree[rt].val; if(rson(rt)!=0) { tree[rt].mx=tree[rson(rt)].mx; fa(rson(rt))=rt; } else tree[rt].mx=tree[rt].val; } void pushlazy(int rt,int add,int rev) { if(rt==0) return; if(rev==1) { tree[rt].rev^=1; tree[rt].add=-tree[rt].add+add; tree[rt].val=-tree[rt].val+add; tree[rt].mn=-tree[rt].mn+add; tree[rt].mx=-tree[rt].mx+add; swap(tree[rt].mn,tree[rt].mx); } else { tree[rt].add+=add; tree[rt].val+=add; tree[rt].mn+=add; tree[rt].mx+=add; } } void pushdown(int rt) { if(tree[rt].rev==1) swap(lson(rt),rson(rt)); pushlazy(lson(rt),tree[rt].add,tree[rt].rev); pushlazy(rson(rt),tree[rt].add,tree[rt].rev); tree[rt].add=tree[rt].rev=0; } void split(int rt,int val,int &x,int &y) { if(rt==0) { x=y=0; return; } pushdown(rt); if(tree[rt].val<=val) { x=rt; split(rson(rt),val,rson(x),y); } else { y=rt; split(lson(rt),val,x,lson(y)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; pushdown(rt1); pushdown(rt2); if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } int join(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].mn>tree[rt2].mx) return merge(rt2,rt1); if(tree[rt1].mx<tree[rt2].mn) return merge(rt1,rt2); if(tree[rt1].rnd>tree[rt2].rnd) swap(rt1,rt2); pushdown(rt1); pushdown(rt2); int x,y,z; split(rt2,tree[rt1].val,x,y); split(x,tree[rt1].val-1,x,z); if(z!=0) { tree[rt1].cnt+=tree[z].siz; tree[rt1].siz+=tree[z].siz; id[z]=rt1; } lson(rt1)=join(lson(rt1),x); rson(rt1)=join(rson(rt1),y); pushup(rt1); return rt1; } void build() { rt_sum=root=0; for(int i=0;i<=100000;i++) { it[i]=build_rt(i); root=merge(root,i); } } void update(int val) { int x,y; split(root,val,x,y); pushlazy(y,-val,0); pushlazy(x,val,1); root=join(x,y); } void dfs(int rt) { if(rt==0) return; ans[rt]=tree[rt].val; pushdown(rt); dfs(lson(rt)); dfs(rson(rt)); } }T; void init(int n) { klen=3000; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { T.build(); for(int j=L[i];j<=R[i];j++) { pos[j]=i; T.update(a[j]); } T.dfs(T.root); for(int j=0;j<=100000;j++) f[i][j]=ans[T.find(it[j])]; } } int query(int l,int r,int x) { if(pos[l]==pos[r]) for(int i=l;i<=r;i++) x=abs(x-a[i]); else { for(int i=l;i<=R[pos[l]];i++) x=abs(x-a[i]); for(int i=pos[l]+1;i<=pos[r]-1;i++) x=f[i][x]; for(int i=L[pos[r]];i<=r;i++) x=abs(x-a[i]); } return x; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,l,r,x,ans=0,i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&a[i]); init(n); for(i=1;i<=m;i++) { scanf("%d%d%d",&l,&r,&x); l^=ans; r^=ans; x^=ans; printf("%d\n",ans=query(l,r,x)); } return 0; }
\(H\) CF702F T-Shirts
\(I\) CF1172F Nauuo and Bug
-
\(FHQ-Treap\) 值域有交合并维护。
点击查看代码
ll ans[200010]; int a[1000010],it[200010],p; pair<int,int>ql[200010],qr[200010]; struct BST { int root,rt_sum; struct FHQ_Treap { ll val,lazy; int son[2],fa,rnd,cnt,siz; }tree[200010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define fa(rt) (tree[rt].fa) BST() { rt_sum=0; srand(time(0)); } int build_rt(int val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=fa(rt_sum)=tree[rt_sum].lazy=0; tree[rt_sum].val=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=1; return rt_sum; } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; fa(rt)=0; if(lson(rt)!=0) fa(lson(rt))=rt; if(rson(rt)!=0) fa(rson(rt))=rt; } void pushlazy(int rt,ll lazy) { tree[rt].lazy+=lazy; tree[rt].val+=lazy; } void pushdown(int rt) { pushlazy(lson(rt),tree[rt].lazy); pushlazy(rson(rt),tree[rt].lazy); tree[rt].lazy=0; } void split(int rt,ll val,int &x,int &y) { if(rt==0) { x=y=0; return; } pushdown(rt); if(tree[rt].val<=val) { x=rt; split(rson(rt),val,rson(x),y); } else { y=rt; split(lson(rt),val,x,lson(y)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; pushdown(rt1); pushdown(rt2); if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } int join(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; int rt=0; for(;rt2!=0;swap(rt1,rt2)) { int x=rt2; for(;lson(x)!=0;x=lson(x)) pushdown(x); split(rt1,tree[x].val,x,rt1); rt=merge(rt,x); } rt=merge(rt,rt1); return rt; } int insert(int val) { int x,y; split(root,val,x,y); root=merge(merge(x,build_rt(val)),y); return rt_sum; } void update(int val) { pushlazy(root,val); int x,y; split(root,p-1,x,y); pushlazy(y,-p); root=join(x,y); } ll query(int rt) { ll ans=tree[rt].val; for(rt=fa(rt);rt!=0;rt=fa(rt)) ans+=tree[rt].lazy; return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,x,y,i,j,k; scanf("%lld%lld%lld",&n,&m,&p); for(i=1;i<=n;i++) scanf("%lld",&a[i]); for(i=1;i<=m;i++) { scanf("%lld%lld",&x,&y); ql[i]=make_pair(x,i); qr[i]=make_pair(y,i); } sort(ql+1,ql+1+m); sort(qr+1,qr+1+m); for(i=j=k=1;i<=n;i++) { for(;ql[j].first==i;j++) it[ql[j].second]=T.insert(0); T.update(a[i]); for(;qr[k].first==i;k++) ans[qr[k].second]=T.query(it[qr[k].second]); } for(i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }
\(J\) luogu P10147 [Ynoi1999] 56TP
\(K\) luogu P8337 [Ynoi2004] rsxc
\(L\) luogu P9057 [Ynoi2004] rpfrdtzls
\(M\) luogu P3316 [SDOI2014] 里面还是外面
\(N\) luogu P11370 [Ynoi2024] 堕天作战/虚空处刑
\(O\) luogu P11369 [Ynoi2024] 弥留之国的爱丽丝
\(P\) luogu P11367 [Ynoi2024] 魔法少女网站第二部
\(Q\) luogu P10151 [Ynoi1999] SMV CC-64“蝰蛇”
\(R\) luogu P10150 [Ynoi1999] TS-54
\(S\) luogu P10028 [Ynoi2000] pri
\(T\) luogu P9996 [Ynoi2000] hpi
\(U\) luogu P9337 [Ynoi2001] 冷たい部屋、一人
\(V\) luogu P9062 [Ynoi2002] Adaptive Hsearch&Lsearch
\(W\) CF1100F Ivan and Burgers
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18671745,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。