2018ICPC首尔A题 Circuits(线段树)
经典套路,首先发现只有两个边,这种情况下,很容易想到使用枚举的方法,枚举第一条边,然后计算对应的第二边最优,之后对于所有情况取max
这里只有y有用并且每个矩形其实就是一条线段
对于这题,一个需要考虑的问题是,如何当我们枚举一条边的时候,计算第二边答案的时候不会计算进第一条已经穿过的矩形。
那么其实这也是另一个套路,只要我们动态加边,倒着枚举,枚举每个点时,线段树中只有后面不会被当前点穿过的边,然后再维护一个前缀和,表示每个点能穿过的边
两个答案相加就是当前枚举点的答案
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=2e5+10; vector<int> num; vector<int> g[N]; int l[N],r[N]; int sum[N]; struct node{ int l,r; int mx; int lazy; }tr[N<<2]; int find(int x){ return lower_bound(num.begin(),num.end(),x)-num.begin()+1; } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r}; } else{ tr[u]={l,r}; int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } } void pushdown(int u){ int x=tr[u].lazy; tr[u<<1].mx+=x; tr[u<<1|1].mx+=x; tr[u<<1].lazy+=x; tr[u<<1|1].lazy+=x; tr[u].lazy=0; } void pushup(int u){ tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx); } void modify(int u,int l,int r,int x){ if(tr[u].l>=l&&tr[u].r<=r){ tr[u].mx+=x; tr[u].lazy+=x; return ; } if(tr[u].lazy) pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l<=mid) modify(u<<1,l,r,x); if(r>mid) modify(u<<1|1,l,r,x); pushup(u); } int main(){ int n; ios::sync_with_stdio(false); cin>>n; int i; for(i=1;i<=n;i++){ int a,b,c,d; cin>>a>>b>>c>>d; l[i]=d,r[i]=b; num.push_back(b); num.push_back(d); } sort(num.begin(),num.end()); num.erase(unique(num.begin(),num.end()),num.end()); for(i=1;i<=n;i++){ l[i]=find(l[i]); r[i]=find(r[i]); g[l[i]].push_back(r[i]); sum[l[i]]++,sum[r[i]+1]--; } for(i=1;i<=(int)num.size();i++){ sum[i]+=sum[i-1]; } int ans=0; build(1,1,(int)num.size()); for(i=(int)num.size();i>=1;i--){ ans=max(ans,sum[i]+tr[1].mx); for(auto x:g[i]){ modify(1,i,x,1); } } cout<<ans<<endl; }
没有人不辛苦,只有人不喊疼