-->node
• 分別用V0、V1和V>=2表示度爲0、1以及至少爲2的頂點集合算法
• 對於每一個頂點,維護三個屬性:數據結構
• degree 鄰居的個數ui
• degree2 鄰居中度爲2的頂點數spa
• id 編號code
• initialize V0, V1, V>=2 and (degree, degree2, id) of each nodeorm
• while G is not emptyblog
• if V0 is not empty索引
• choose v ∈ V0 with the smallest idget
• output v, delete v from V0 and G
• else if V1 is not empty
• choose v ∈ V1 with the smallest id, and find the neighbor u of v
• output v, delete v from V0 and G, delete u from V1(or V>=2) and G
• else
• choose v ∈ V>=2 with the largest (degree, degree2, id)
• delete v from V>=2 and G
• 須要注意的是,每一個頂點的屬性以及頂點集合V0、V1和V>=2並不是一成不變。
• 當從圖中刪去某個頂點u時,u鄰居的degree均會減一;若是u的degree剛好爲2,那麼u鄰居的degree2也會減一。
• 若是某個鄰居v的degree剛好從3減小到2或從2減到1,那麼還會進一步影響到v的鄰居的degree2屬性。
• 對於那些degree減一的頂點,還須要相應地更新V0、V1和V>=2。
• for v ∈ Neighbor(u)
• v.degree decreases by one
• if u.degree == 2 then v.degree2 decreases by one
• if v.degree == 0
• move v from V1 to V0
• else if v.degree == 1
• move v from V>=2 to V1
• find the only neighbor w of v
• w.degree2 decreases by one
• else if v.degree == 2
• for w ∈ Neighbor(v) do w.degree2 increases by one
V0和V1 {node_id}
• 插入、刪除頂點,但每一個頂點最多一次;
• 查詢id最小的頂點。
• 頂點數量O(N)
• 插入刪除操做次數O(N)
• 查詢最值次數O(N)
V>=2 {<node_id, value>}
• 初始化以後不會再插入頂點;
• 刪除頂點,每一個最多一次;
• 查詢屬性值最大的頂點;
• 修改一個點的屬性值。
• 頂點數量O(N)
• 刪除操做次數O(N)
• 查詢最值次數O(N)
• 修改屬性次數O(M)
• 堆、線段樹、平衡樹等等……
• 增、刪、改、查均可以在O(logN)複雜度內完成
• 時間複雜度:O((N+M)logN)
• 懶得敲一個的話就用經濟實惠的priority_queue好了。雖然不能直接進行刪除或修改,但能夠用懶更新的方式解決。即等到取用最值的時候,再判斷該頂點是否已經被刪除或屬性已經被修改過了。
#include<queue> #include<cstdio> const int N=1e5+5; const int M=N*10; template <typename T> inline void read(T &x){ T f=1;char ch=getchar();x=0; while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } struct node{ int id,degree,nxt2; node(int id=0,int degree=0,int nxt2=0):id(id),degree(degree),nxt2(nxt2){} bool operator <(const node &a){ // id < nxt2 < degree return degree!=a.degree?degree<a.degree:nxt2!=a.nxt2?nxt2<a.nxt2:id<a.id; } }b[N]; template<class nodeT> struct segtree{ int n;nodeT* a; segtree(){} segtree(int _n,const nodeT* b):n(_n){ a=new nodeT[n<<2|1]; build(1,1,n,b); } #define lch k<<1 #define rch k<<1|1 #define max(a,b) ((a)<(b)?b:a) inline void updata(int k){ a[k]=max(a[lch],a[rch]); } void build(int k,int l,int r,const nodeT* b){ if(l==r){ a[k]=b[l]; return ; } int mid=l+r>>1; build(lch,l,mid,b); build(rch,mid+1,r,b); updata(k); } void change(int k,int l,int r,int p,const nodeT &v){ if(l==r){ a[k]=v; return ; } int mid=l+r>>1; if(p<=mid) change(lch,l,mid,p,v); else change(rch,mid+1,r,p,v); updata(k); } inline void change(int p,const nodeT &v){ change(1,1,n,p,v); } inline void del(int p){ nodeT t(-1,-1,-1); change(1,1,n,p,t); } inline nodeT& query(){ return a[1]; } #undef lch #undef rch #undef max(a,b) }; int cnt,ans[N]; int n,m,tot,to[M],next[M],head[N],degree[N];int del[N]; std::priority_queue<int,std::vector<int>,std::greater<int> >q0,q1; inline void add(int x,int y){ to[++tot]=y;next[tot]=head[x];head[x]=tot; } inline void addedge(int x,int y){ add(x,y);degree[x]++; add(y,x);degree[y]++; } inline void Erase(int u,segtree<node> &tree){ del[u]=1; tree.del(u); for(int j=head[u],v;j;j=next[j]){ if(del[v=to[j]]) continue; b[v].degree--; if(b[u].degree==2) b[v].nxt2--; tree.change(v,b[v]); if(b[v].degree==2||b[v].degree==1){ for(int k=head[v],w;k;k=next[k]){ if(del[w=to[k]]) continue; b[w].nxt2+=b[v].degree*2-3; tree.change(w,b[w]); } } if(b[v].degree==0) q0.push(v); if(b[v].degree==1) q1.push(v); } } inline void Init(){ read(n);read(m); for(int i=1,x,y;i<=m;i++) read(x),read(y),addedge(x,y); } inline void Solve(){ for(int i=1;i<=n;i++) b[i].id=i,b[i].degree=degree[i],b[i].nxt2=0; for(int i=1;i<=n;i++){ if(degree[i]==2){ for(int j=head[i];j;j=next[j]){ b[to[j]].nxt2++; } } } segtree<node> tree(n,b); for(int i=1;i<=n;i++){ if(!degree[i]){ del[i]=1; tree.del(i); ans[++cnt]=i; }else if(degree[i]==1) q1.push(i); } for(int u,v;;){ if(!q0.empty()){ u=q0.top();q0.pop(); if(del[u]) continue; del[u]=1; tree.del(u); ans[++cnt]=u; } else if(!q1.empty()){ u=q1.top();q1.pop(); if(del[u]) continue; for(int i=head[u];i;i=next[i]) if(!del[v=to[i]]) break; Erase(u,tree); Erase(v,tree); ans[++cnt]=u; } else{ int tid=tree.query().id; if(~tid) Erase(tid,tree);else break; } } for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]); } int main(){ freopen("greedy.in","r",stdin); freopen("greedy.out","w",stdout); Init(); Solve(); return 0; }