ACM-ICPC 2018 徐州赛区网络预赛-G Trace(线段树的应用
Problem:Portal传送门
原题目描述在最下面。
我理解的题意大概是:有n次涨潮和退潮,每次的范围是个x×y的矩形,求n次涨退潮后,潮水痕迹的长度。
不存在此i,j∈[1,n],i≠j,xi≤xj且yi≤y
Solution:
每次潮水可能会冲刷掉之前的潮水的一部分痕迹,但是它又不会完全冲刷。
考虑从最后一次潮水开始往前算贡献,分x,y方向计算。
假设此次潮水的范围是[xi,yi],找出大于等于xi的最大Y,则y方向的新增痕迹长度为yi−Y,xx方向同理。
这样问题就转化为一个求区间最值的问题了,线段树秒。
#include<bits/stdc++.h> #define lson rt<<1 #define rson rt<<1|1 using namespace std; typedef long long LL; typedef unsigned long long uLL; const int mod = 1e9+7; const int MXN = 1e6 + 7; int n; int sumx[MXN<<2], sumy[MXN<<2]; int ar[MXN], br[MXN], le[MXN], ri[MXN]; void update(int op,int p,int c,int l,int r,int rt){ if(l == r){ if(op == 1) sumx[rt] = max(c,sumx[rt]); else sumy[rt] = max(c,sumy[rt]); return; } int mid = (l+r)>>1; if(p<=mid)update(op,p,c,l,mid,lson); else update(op,p,c,mid+1,r,rson); if(op == 1)sumx[rt] = max(sumx[lson], sumx[rson]); else sumy[rt] = max(sumy[lson], sumy[rson]); } int query(int op,int L,int R,int l,int r,int rt){ if(L<=l&&r<=R){ if(op == 1) return sumx[rt]; else return sumy[rt]; } int mid = (l+r)>>1; if(L>mid)return query(op,L,R,mid+1,r,rson); else if(R<=mid)return query(op,L,R,l,mid,lson); else { return max(query(op,L,mid,l,mid,lson),query(op,mid+1,R,mid+1,r,rson)); } } int main(int argc, char const *argv[]){ scanf("%d", &n); int k = 0; for(int i = 1; i <= n; ++i){ scanf("%d%d", &le[i], &ri[i]); ar[k++] = le[i]; ar[k++] = ri[i]; } sort(ar, ar + k); k = unique(ar, ar + k) - ar; memset(sumx, 0, sizeof(sumx)); memset(sumy, 0, sizeof(sumy)); LL ans = 0; for(int i = n; i >= 1; --i){ int a = le[i], b = ri[i]; le[i] = lower_bound(ar, ar + k, le[i]) - ar + 1; ri[i] = lower_bound(ar, ar + k, ri[i]) - ar + 1; int my = query(1, le[i], k, 1, k, 1);//找出大于xi的最大Y ans += b - my; int mx = query(2, ri[i], k, 1, k, 1);//找出大于yi的最大X ans += a - mx; //printf("%d %d\n", mx, my); update(1, le[i], ar[ri[i]-1], 1, k, 1);//更新此xi下的yi update(2, ri[i], ar[le[i]-1], 1, k, 1); } printf("%lld\n", ans); return 0; }
还有一种做法 : 一样是从后面开始 在考虑第I个浪的贡献时候,Xi产生的贡献是(xi , 无限)的最大值MX , ans+=xi-MAX;y同理
#include <bits/stdc++.h> #define ll long long using namespace std; const int N=500005; int x[N],y[N],n; int xt[N],yt[N]; int treex[N],treey[N]; int sum(int i,int op) { int res=0; while(i) { if(op==1) res=max(res,treex[i]); else res=max(res,treey[i]); i -= -i&i; } return res; } void add(int i,int id,int op) { while(i<=N) { if(op==1) treex[i]=max(treex[i],id); else treey[i]=max(treey[i],id); i += -i&i; } } int main() { scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d%d",&x[i],&y[i]); xt[i]=x[i], yt[i]=y[i]; } sort(xt,xt+n); sort(yt,yt+n); ll ans=0; for(int i=n-1;i>=0;i--) { int xi=lower_bound(xt,xt+n,x[i])-xt+1; int yi=lower_bound(yt,yt+n,y[i])-yt+1; //printf("%d %d\n",xi,yi); ans+=(x[i]-sum(xi,1)); ans+=(y[i]-sum(yi,0)); add(xi,x[i],1); add(yi,y[i],0); } printf("%lld\n",ans); return 0; }