5 条题解
-
4
观察题目,要我们考虑在不同海平面的情况下讨论“岛屿”的个数
因为如果按正常顺序一会加一会减很麻烦,所以我们很容易就想到给询问按x从小到大排序,具有单调性。 同时h也排序,记录id。
之后枚举每个询问,改变那些要变成0的h,此时,我们要弄清楚它们分别属于那些岛屿,所以联想到开集合L和R,记录每个岛屿的l和r。
此时用二分找到对应的l与r,然后看id在岛屿中具体处于什么位置,对L与R集合做出具体改变(详见代码)
最后解决当前询问的l和r,注意到在L和R中的l[i]与r[i]是一一对应的,所以查询l和r分别在R与L集合中处于什么位置,注意到集合set实现此操作需要O(N)的时间复杂度,所以考虑pb_ds,用O(logN)实现此操作,最终将两位置相减得到最终答案。
#include<bits/stdc++.h> #include<bits/extc++.h> using namespace std; using namespace __gnu_pbds; typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> ordered_set; const int N=2e5+10; struct node { int h,id; }a[N]; bool cmp(node A,node B) { return A.h<B.h; } int h[N]; struct node1 { int l,r,x,id; }t[N]; bool cmp1(node1 A,node1 B) { return A.x<B.x; } int ans[N]; ordered_set L,R;//pb_ds int main() { int n,q; scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) { scanf("%d",&h[i]); a[i].h=h[i]; a[i].id=i; } for(int i=1;i<=n;i++) { if(h[i]&&!h[i-1]) { L.insert(i); } if(h[i]&&!h[i+1]) { R.insert(i); } }//注意此处初始化不能只塞入(1,n),要防止某个h[i]=0同时询问的x为0的情况 sort(a+1,a+n+1,cmp);//按h排序 sort(h+1,h+n+1); for(int i=1;i<=q;i++) { scanf("%d%d%d",&t[i].l,&t[i].r,&t[i].x); t[i].id=i; } sort(t+1,t+q+1,cmp1);//按x排序 int last=0;//记录之前最后改变为0的id for(int i=1;i<=q;i++) { int x=t[i].x; int p=upper_bound(h+1,h+n+1,x)-h-1; if(p>last) { for(int j=last+1;j<=p;j++)//O(NlogN)的改变 { if(h[j]==0) { continue; } int id=a[j].id; auto ll=L.upper_bound(id); ll--; auto rr=R.lower_bound(id); int l=*ll; int r=*rr; if(l==r) { L.erase(l); R.erase(r); } else if(l==id) { L.erase(l); L.insert(l+1); } else if(r==id) { R.erase(r); R.insert(r-1); } else { L.insert(id+1); R.insert(id-1); }//具体的处理操作 } } last=p; int l=t[i].l,r=t[i].r; int ll=R.order_of_key(l)+1,rr=L.order_of_key(r+1); ans[t[i].id]=rr-ll+1;//记录原id的ans } for(int i=1;i<=q;i++) { printf("%d\n",ans[i]); } return 0; }//注:此代码纯乱搞,无算法 -
2
思路
很容易发现如果按照正常的顺序计算问题,海平面上上下下,不是很方便计算,因此考虑进行排序使得海平面单调下降,方便计算。如此一来,一个山峰露出来我们需要将他标记一下,顺便计算他对答案的贡献。单点修改,区间查询,考虑使用线段树维护。
在一个树节点中维护三个变量:分别表示这一区间内岛屿的数量,区间左右端点是否露出来了,具体的转移及查询操作见代码。
AC代码
#include<bits/stdc++.h> #define lc(p) (p<<1) #define rc(p) (p<<1|1) using namespace std; const int N=2e5+10; struct node{int l,r,x,id;}q[N];//存储询问 int ans[N]; struct nod{int x,id;}a[N];//存储山峰 struct trnode{int l,r,cnt;bool isl,isr;}tr[N<<2];//存储查询 void pu(int p) { tr[p].cnt=tr[lc(p)].cnt+tr[rc(p)].cnt-(tr[lc(p)].isr&&tr[rc(p)].isl);//记得减去中间重复的情况 tr[p].isl=tr[lc(p)].isl;tr[p].isr=tr[rc(p)].isr; } void build(int p,int l,int r) { tr[p].l=l,tr[p].r=r; if(l==r)return ; int mid=l+r>>1; build(lc(p),l,mid);build(rc(p),mid+1,r); } void change(int p,int x) { if(tr[p].r<x||x<tr[p].l)return ; if(tr[p].l==tr[p].r) { tr[p].cnt=tr[p].isl=tr[p].isr=1;//就是这个岛 return ; } change(lc(p),x); change(rc(p),x); pu(p); } trnode query(int p,int l,int r)//实在不想再开新的结构体了,用trnode将就一下 { if(tr[p].r<l||r<tr[p].l)return {0,0,0,0,0}; if(l<=tr[p].l&&tr[p].r<=r)return {0,0,tr[p].cnt,tr[p].isl,tr[p].isr}; trnode n1=query(lc(p),l,r),n2=query(rc(p),l,r); return {0,0,n1.cnt+n2.cnt-(n1.isr&&n2.isl),n1.isl,n2.isr}; } int main() { int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i].x),a[i].id=i; sort(a+1,a+n+1,[](nod n1,nod n2){return n1.x<n2.x;});//对山峰进行排序,方便后续计算 for(int i=1;i<=m;i++)scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].x),q[i].id=i; sort(q+1,q+m+1,[](node n1,node n2){return n1.x>n2.x;}); build(1,1,n);//千万别忘了建树!!! for(int i=1,j=n;i<=m;i++) { while(j>=1&&a[j].x>q[i].x)change(1,a[j].id),j--;//双指针找寻那些山峰该露出来 ans[q[i].id]=query(1,q[i].l,q[i].r).cnt; } for(int i=1;i<=m;i++)printf("%d\n",ans[i]); return 0;//完结撒花~ } -
1
比较接近线段树模板吧,除了 函数有小小的改动。
#include<bits/stdc++.h> using namespace std; #define lp (p<<1) #define rp (p<<1|1) const int N=2e5+10; struct trnd{int l,r,cnt;bool ls,rs;}tr[N<<2]; struct nd{int pos,h;}a[N]; struct qnd{int id,l,r,x,ans;}ask[N]; bool cmp1(nd n1,nd n2){return n1.h>n2.h;} bool cmp2(qnd n1,qnd n2){return n1.x>n2.x;} bool cmp3(qnd n1,qnd n2){return n1.id<n2.id;} void pushup(int p) { tr[p].cnt=tr[lp].cnt+tr[rp].cnt-(tr[lp].rs&&tr[rp].ls); tr[p].ls=tr[lp].ls;tr[p].rs=tr[rp].rs; } void bt(int p,int l,int r) { tr[p]={l,r,0,0,0}; if(l==r)return; int mid=(l+r)>>1; bt(lp,l,mid); bt(rp,mid+1,r); pushup(p); } void change(int p,int x) { if(x<tr[p].l||tr[p].r<x)return; if(tr[p].l==tr[p].r) { tr[p].cnt=tr[p].ls=tr[p].rs=1; return; } change(lp,x); change(rp,x); pushup(p); } int query(int p,int l,int r) { if(r<tr[p].l||tr[p].r<l)return -1; if(l<=tr[p].l&&tr[p].r<=r)return tr[p].cnt; int lcnt=query(lp,l,r),rcnt=query(rp,l,r); if(lcnt!=-1&&rcnt!=-1)return lcnt+rcnt-(tr[lp].rs&&tr[rp].ls); if(lcnt==-1)lcnt=0; if(rcnt==-1)rcnt=0; return lcnt+rcnt; } int main() { int n,q;scanf("%d%d",&n,&q); for(int i=1,h;i<=n;i++) { scanf("%d",&h); a[i]={i,h}; } sort(a+1,a+n+1,cmp1); for(int i=1,l,r,x;i<=q;i++) { scanf("%d%d%d",&l,&r,&x); ask[i]={i,l,r,x,0}; } sort(ask+1,ask+q+1,cmp2); int id=1;bt(1,1,n); for(int i=1;i<=q;i++) { while(id<=n&&a[id].h>ask[i].x)change(1,a[id].pos),id++; ask[i].ans=query(1,ask[i].l,ask[i].r); } sort(ask+1,ask+q+1,cmp3); for(int i=1;i<=q;i++)printf("%d\n",ask[i].ans); return 0; } -
1
解法
一组询问
先考虑一个简化版的情况,如果只有一组询问怎么处理。
那不直接打暴力可以考虑一个常见的技巧:每个位置赋一个颜色 ,满足:
$$c_i=\begin{cases} 1 & h_i>x\\ 0 & h_i\leq x \end{cases}$$这样我们就把原来的序列变成了一个 01 串。
问题就变成了 01 串上给定区间 中连续的 的区间的个数。
多组询问
有个显然的结论:如果 单调下降,则生成的 01 串中只会有 变成 。
在这种情况下问题就变成了单点修改,区间查询连续的 的区间的个数。
直接线段树。解决。
所以做法就是先将询问离线,按 从大到小排序。
每次先加入当前高度下新加入的节点, 然后查询 间的连续的 的区间的个数。
每次新加入的节点可以用桶预先统计好。
Code
#include<bits/stdc++.h> using namespace std; #define maxn 200005 struct SegT { struct node { int l=0, r=0, ans=0; node operator+(node b) {return {l, b.r, ans+b.ans-(r&&r==b.l)};} void operator=(int b) {l=r=ans=b;} }tr[maxn<<2]; #define lc (x<<1) #define rc (x<<1|1) #define mid ((l+r)>>1) #define lson lc, l, mid #define rson rc, mid+1, r #define rt 1, 1, n void push_up(int x) {tr[x]=tr[lc]+tr[rc];} void modify(int x, int l, int r, int p) { if(l==r) return tr[x]=1; if(p<=mid) modify(lson, p); if(p>mid) modify(rson, p); push_up(x); } node query(int x, int l, int r, int L, int R) { if(L<=l&&r<=R) return tr[x]; if(R<=mid) return query(lson, L, R); if(L>mid) return query(rson, L, R); return query(lson, L, R)+query(rson, L, R); } }tr; // 线段树部分 vector<tuple<int, int, int, int>> qrs; // 储存离线询问 vector<int> adds[maxn] /*存储每次新加入的节点*/, hgt; int lis[maxn], ans[maxn]; int main() { int n, q; cin>>n>>q; for(int i=1;i<=n;i++) cin>>lis[i]; for(int i=1, l, r, x;i<=q;i++) cin>>l>>r>>x, qrs.emplace_back(x, l, r, i), hgt.emplace_back(x); sort(qrs.begin(), qrs.end(), greater()); sort(hgt.begin(), hgt.end(), greater()); for(int i=1;i<=n;i++) adds[upper_bound(hgt.begin(), hgt.end(), lis[i], greater())-hgt.begin()+1].emplace_back(i); // 向桶内添加节点 int cnt=1; for(auto [h, l, r, i]:qrs) { for(auto v:adds[cnt++]) tr.modify(rt, v); ans[i]=tr.query(rt, l, r).ans; // 先改再查 } for(int i=1;i<=q;i++) cout<<ans[i]<<'\n'; } -
0
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n,q,a[200010],vis[200010]; int lowbit(int x){ return x&(-x); } struct NN{ int v,x; }b[200010]; bool cmpb(NN a,NN b){ return a.v<b.v; } struct N{ int tr[200010]; void add(int x,int v){ for(int i=x;i<=n;i+=lowbit(i)){ tr[i]+=v; } } int find(int x){ int ans=0; for(int i=x;i;i-=lowbit(i)){ ans+=tr[i]; } return ans; } }tr,tr2; struct Q{ int l,r,x,id; }c[200010]; bool cmp(Q a,Q b){ return a.x<b.x; } int ans[200010]; int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>q; int la=0; for(int i=1;i<=n;i++){ cin>>a[i]; b[i]={a[i],i}; if(a[i]){ if(!la){ tr.add(i,1); } la=1; } else{ if(la)tr2.add(i-1,1); la=0; } } if(la)tr2.add(n,1); sort(b+1,b+1+n,cmpb); for(int i=1;i<=q;i++){ cin>>c[i].l>>c[i].r>>c[i].x; c[i].id=i; } sort(c+1,c+1+q,cmp); int x=1; while(x<=n&&!b[x].v){ vis[b[x].x]=1;x++; } for(int i=1;i<=q;i++){ while(x<=n&&b[x].v<=c[i].x){ int p=b[x].x; if(p==1){ if(vis[p+1]){ tr.add(p,-1); tr2.add(p,-1); } else{ tr.add(p,-1); tr.add(p+1,1); } } else if(p==n){ if(vis[p-1]){ tr.add(p,-1); tr2.add(p,-1); } else{ tr2.add(p,-1); tr2.add(p-1,1); } } else{ if(vis[p-1]&&vis[p+1]){ tr.add(p,-1); tr2.add(p,-1); } else if(vis[p-1]){ tr.add(p,-1); tr.add(p+1,1); } else if(vis[p+1]){ tr2.add(p,-1); tr2.add(p-1,1); } else{ tr.add(p+1,1); tr2.add(p-1,1); } } vis[p]=1; x++; } ans[c[i].id]=tr.find(c[i].r)-tr2.find(c[i].l-1); } for(int i=1;i<=q;i++){ cout<<ans[i]<<'\n'; } return 0; }
- 1
信息
- ID
- 7309
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 47
- 已通过
- 15
- 上传者