数据结构
开题顺序: \(WHABCEI\)
\(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 Isaacfreopen("in.in","r",stdin);freopen("out.out","w",stdout); #endifint 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 Isaacfreopen("in.in","r",stdin);freopen("out.out","w",stdout); #endifint 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 Isaacfreopen("in.in","r",stdin);freopen("out.out","w",stdout); #endifint 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 Isaacfreopen("in.in","r",stdin);freopen("out.out","w",stdout); #endifint 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
\(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 Isaacfreopen("in.in","r",stdin);freopen("out.out","w",stdout); #endifint 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
- 题解