1. 程式人生 > >【XSY2707】snow 線段樹 並查集

【XSY2707】snow 線段樹 並查集

return pre 觀察 就是 輸出 時間復雜度 連續 mar util

題目描述

  有\(n\)個人和一條長度為\(t\)的線段,每個人還有一個工作範圍(是一個區間)。最開始整條線段都是白的。定義每個人的工作長度是這個人的工作範圍中白色部分的長度(會隨著線段改變而改變)。每一天開始時你要選擇一個人滿足這個人的工作長度最小(如果有多個就選編號最小的)。把這個人的工作區間染黑。請你輸出每天你選了哪個人。

  保證工作範圍中左端點和右端點單調遞增。

  \(n\leq 300000\)

題解

  先把線段離散化成很多個小區間,那麽每個小區間只會被染黑一次(染黑之後不會變白)。

  因此每次選擇了一個人後可以暴力把這個人的工作範圍內白色的區間染黑,那麽覆蓋了這個區間的人就會受到影響。

  觀察到左右端點都單調遞增,所以覆蓋一個區間的人就是連續的。

  直接用線段樹維護每個人當前的工作長度。

  用並查集維護右邊的第一個白色區間。

  時間復雜度:\(O(n\log n)\)

代碼

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<utility>
#include<cstring>
using namespace std;
typedef pair<int,int> pii;
int a[2000010];
int c[2000010];
int
d[2000010]; int f[2000010]; int l[2000010]; int r[2000010]; int s[2000010]; int rt; namespace seg { struct node { int l,r,ls,rs; int t; pii s; node() { l=r=ls=rs=t=0; } }; node a[2000010]; int cnt; void build(int &p,int l,int r) { p=++cnt; a[p].l=l; a[p].r=r; if
(l==r) { a[p].s=pii(::s[l],l); return; } int mid=(l+r)>>1; build(a[p].ls,l,mid); build(a[p].rs,mid+1,r); a[p].s=min(a[a[p].ls].s,a[a[p].rs].s); } void add(int p,int v) { a[p].t+=v; a[p].s.first+=v; } void push(int p) { if(a[p].t&&a[p].l!=a[p].r) { add(a[p].ls,a[p].t); add(a[p].rs,a[p].t); a[p].t=0; } } void add(int p,int l,int r,int v) { if(l<=a[p].l&&r>=a[p].r) { add(p,v); return; } push(p); int mid=(a[p].l+a[p].r)>>1; if(l<=mid) add(a[p].ls,l,r,v); if(r>mid) add(a[p].rs,l,r,v); a[p].s=min(a[a[p].ls].s,a[a[p].rs].s); } void gao(int p,int x) { if(a[p].l==a[p].r) { a[p].s.first=0x7fffffff; return; } push(p); int mid=(a[p].l+a[p].r)>>1; if(x<=mid) gao(a[p].ls,x); else gao(a[p].rs,x); a[p].s=min(a[a[p].ls].s,a[a[p].rs].s); } int get(int p,int x) { if(a[p].l==a[p].r) return a[p].s.first; push(p); int mid=(a[p].l+a[p].r)>>1; if(x<=mid) return get(a[p].ls,x); else return get(a[p].rs,x); } } int find(int x) { return f[x]==x?x:f[x]=find(f[x]); } int b[1000010]; int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif int n,t; scanf("%d%d",&t,&n); int m=0; int i; memset(a,0,sizeof a); memset(c,0,sizeof c); memset(d,0,sizeof d); memset(f,0,sizeof f); memset(s,0,sizeof s); memset(l,0,sizeof l); memset(r,0,sizeof r); for(i=1;i<=n;i++) { scanf("%d%d",&l[i],&r[i]); r[i]--; s[i]=r[i]-l[i]+1; a[++m]=l[i]; a[++m]=r[i]+1; } sort(a+1,a+m+1); m=unique(a+1,a+m+1)-a-1; for(i=1;i<=n;i++) { int x=l[i],y=r[i]; l[i]=lower_bound(a+1,a+m+1,l[i])-a; r[i]=lower_bound(a+1,a+m+1,r[i]+1)-a-1; if(a[l[i]]!=x) printf("error\n"); if(a[r[i]+1]!=y+1) printf("error\n"); if(l[i]<=l[i-1]) printf("error\n"); if(r[i]<=r[i-1]) printf("error\n"); } seg::cnt=0; rt=0; seg::build(rt,1,n); for(i=1;i<=m;i++) { c[i]=a[i+1]-a[i]; f[i]=i; } f[m+1]=m+1; int j; for(i=1;i<=n;i++) { pii ans=seg::a[rt].s; // printf("%d %d\n",seg::get(rt,7396),seg::get(rt,20692)); printf("%d\n",ans.second); // seg::add(rt,ans.second,ans.second,0x7fffffff-ans.first); seg::gao(rt,ans.second); // for(j=l[ans.second];j<=r[ans.second];j++) // if(!b[j]) // { // b[j]=1; for(j=find(l[ans.second]);j<=r[ans.second];j=find(j)) { int x=lower_bound(r+1,r+n+1,j)-r; int y=upper_bound(l+1,l+n+1,j)-l-1; if(x<=y) { // if(r[x-1]>=j) // printf("error1\n"); // if(l[y+1]<=j) // printf("error2\n"); seg::add(rt,x,y,-c[j]); } // else // if(l[7396]<=j&&r[7396]>=j) // seg::add(rt,7396,7396,-c[j]); f[j]=j+1; } } return 0; }

【XSY2707】snow 線段樹 並查集