CodeForces - 160D Edges in MST(思维+tarjan/树链剖分+线段树)
題目鏈接:點擊查看
題目大意:給出一張 n 個點 m 條邊組成的帶權無向圖,現在對于每條邊來說,確定一下其分類:
題目分析:兩種思路,一種是比較好想,但是寫起來比較麻煩,還有點卡常,另一種是需要一定的思維,相應的實現起來也是比較簡單
先說需要思維的方法,參考克魯斯卡爾的實現方法,不難看出權值較大的邊一定不可能有機會去替換掉權值較小的邊,所以如果一條邊可能出現在多個最小生成樹上時,一定是可能會被其他同權值的邊替換,所以在實現克魯斯卡爾的過程中,可以將權值相同的邊一起處理,如果某條邊在加入之前兩個點就已經聯通,那么顯然這條邊是多余的,一定不可能是最小生成樹上的邊,否則的話每次重新建圖然后跑 tarjan,如果某條邊是橋的話說明這條邊一定是最小生成樹上的邊,否則就可能同時出現在多個最小生成樹上,實現起來比較簡單
另一種方法的話就很像另一個題目了:CodeForces - 609E
就是先隨便求出一個最小生成樹,樹剖一下用線段樹維護一下最大值,然后枚舉所有的非樹邊,當加入一條非樹邊 ( x , y , w ) 后,一定會在最小生成樹上生成一個環,此時我們找一下原來 x 到 y 的路徑上的最大值記為 mmax,如果:
關于第二種情況上的替換,用線段樹暴力打上標記就好了,記得剪一下枝,有點卡常,加了個 inline 才過的
代碼:
tarjan
樹鏈剖分+線段樹
// #pragma GCC optimize(2) // #pragma GCC optimize("Ofast","inline","-ffast-math") // #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> #include<unordered_map> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;const string str[]={"none","any","at least one"};int f[N],ans[N],n,m;struct Edge {int u,v,w,id,flag;//flag:是否作為最小生成樹中的邊void input(int i){scanf("%d%d%d",&u,&v,&w);id=i;}bool operator<(const Edge& t)const{return w<t.w;} }e[N];vector<Edge>node[N]; /*克魯斯卡爾*/ int find(int x) {return f[x]==x?x:f[x]=find(f[x]); }bool merge(int x,int y) {int xx=find(x),yy=find(y);if(xx!=yy){f[xx]=yy;return true;}return false; }void addedge(int u,int v,int w,int id) {node[u].push_back({u,v,w,id});node[v].push_back({v,u,w,id}); }void Kruskal() {for(int i=1;i<=n;i++)f[i]=i;sort(e+1,e+1+m);for(int i=1;i<=m;i++)if(merge(e[i].u,e[i].v)){addedge(e[i].u,e[i].v,e[i].w,e[i].id);e[i].flag=true;ans[e[i].id]=1;} } /*克魯斯卡爾*/ /*樹鏈剖分的初始化*/ int fa[N],deep[N],num[N],son[N],val[N],_id[N];void dfs1(int u,int f,int dep) {deep[u]=dep;num[u]=1;son[u]=-1;fa[u]=f;for(auto it:node[u]){int v=it.v,w=it.w,id=it.id;if(v==f)continue;val[v]=w;_id[v]=id;dfs1(v,u,dep+1);num[u]+=num[v];if(son[u]==-1||num[son[u]]<num[v])son[u]=v;} }int top[N],id[N],idd[N],cnt;void dfs2(int u,int fa,int root) {top[u]=root;id[u]=++cnt;idd[cnt]=u;if(son[u]!=-1)dfs2(son[u],u,root);for(auto it:node[u]){int v=it.v;if(v==fa||v==son[u])continue;dfs2(v,u,v);} } /*樹鏈剖分的初始化*/ /*線段樹維護區間最值*/ struct Node {int l,r,mmax;bool lazy; }tree[N<<2];void pushdown(int k) {if(tree[k].lazy){tree[k].lazy=false;if(tree[k<<1].mmax==tree[k].mmax)tree[k<<1].lazy=true;if(tree[k<<1|1].mmax==tree[k].mmax)tree[k<<1|1].lazy=true;} }void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].lazy=false;if(l==r){tree[k].mmax=val[idd[l]];return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax); }inline void update(int k,int l,int r,int val)//將最大值等于val的數都打上標記 {if(tree[k].mmax<val)//最大值比閾值還小return;if(tree[k].l>r&&tree[k].r<l)//越界return;if(tree[k].l>=l&&tree[k].r<=r&&tree[k].mmax==val){tree[k].lazy=true;return;}update(k<<1,l,r,val);update(k<<1|1,l,r,val); }void update(int k)//最后更新一下最小生成樹上的at least one {if(tree[k].l==tree[k].r){if(tree[k].lazy)//at least oneans[_id[idd[tree[k].l]]]=2;return;}pushdown(k);update(k<<1);update(k<<1|1); }int query(int k,int l,int r)//返回區間最大值 {if(tree[k].l>r||tree[k].r<l)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].mmax;return max(query(k<<1,l,r),query(k<<1|1,l,r)); } /*線段樹維護區間最值*/ /*樹鏈剖分的路徑操作*/ void update(int x,int y,int val)//將(x-y)路徑上等于val的位置都打上標記 {while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]])swap(x,y);update(1,id[top[x]],id[x],val);x=fa[top[x]];}if(x==y)return;if(deep[x]>deep[y])swap(x,y);update(1,id[x]+1,id[y],val); }int query(int x,int y)//查詢(x-y)路徑上的最大值 {int mmax=0;while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]])swap(x,y);mmax=max(mmax,query(1,id[top[x]],id[x]));x=fa[top[x]];}if(x==y)return mmax;if(deep[x]>deep[y])swap(x,y);mmax=max(mmax,query(1,id[x]+1,id[y]));return mmax; } /*樹鏈剖分的路徑操作*/ int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++)e[i].input(i);Kruskal();dfs1(1,0,0);dfs2(1,0,1);build(1,1,n);for(int i=1;i<=m;i++){if(e[i].flag)//最小生成樹上的邊continue;int mmax=query(e[i].u,e[i].v);if(e[i].w>mmax)//noneans[e[i].id]=0;else//at least one{ans[e[i].id]=2;update(e[i].u,e[i].v,e[i].w);}}update(1);for(int i=1;i<=m;i++)cout<<str[ans[i]]<<endl;return 0; }總結
以上是生活随笔為你收集整理的CodeForces - 160D Edges in MST(思维+tarjan/树链剖分+线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 577B Mo
- 下一篇: CodeForces - 1076D E