日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CodeForces - 160D Edges in MST(思维+tarjan/树链剖分+线段树)

發布時間:2024/4/11 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CodeForces - 160D Edges in MST(思维+tarjan/树链剖分+线段树) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:給出一張 n 個點 m 條邊組成的帶權無向圖,現在對于每條邊來說,確定一下其分類:

  • 一定是最小生成樹上的邊
  • 可能是最小生成樹上的邊
  • 一定不是最小生成樹的邊
  • 題目分析:兩種思路,一種是比較好想,但是寫起來比較麻煩,還有點卡常,另一種是需要一定的思維,相應的實現起來也是比較簡單

    先說需要思維的方法,參考克魯斯卡爾的實現方法,不難看出權值較大的邊一定不可能有機會去替換掉權值較小的邊,所以如果一條邊可能出現在多個最小生成樹上時,一定是可能會被其他同權值的邊替換,所以在實現克魯斯卡爾的過程中,可以將權值相同的邊一起處理,如果某條邊在加入之前兩個點就已經聯通,那么顯然這條邊是多余的,一定不可能是最小生成樹上的邊,否則的話每次重新建圖然后跑 tarjan,如果某條邊是橋的話說明這條邊一定是最小生成樹上的邊,否則就可能同時出現在多個最小生成樹上,實現起來比較簡單

    另一種方法的話就很像另一個題目了:CodeForces - 609E
    就是先隨便求出一個最小生成樹,樹剖一下用線段樹維護一下最大值,然后枚舉所有的非樹邊,當加入一條非樹邊 ( x , y , w ) 后,一定會在最小生成樹上生成一個環,此時我們找一下原來 x 到 y 的路徑上的最大值記為 mmax,如果:

  • w > mmax:當前邊一定不是最小生成樹上的邊
  • w = mmax:當前邊可以替換掉最小生成樹上的權值等于 w 的邊,所以當前邊屬于“可能是最小生成樹上的邊”,同時,最小生成樹上權值等于 w 的邊也從“一定是最小生成樹上的邊”變成了“可能是最小生成樹上的邊”了
  • w < 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],n,m;struct Egde {int to,next,id; }edge[N];int head[N],low[N],dfn[N],ans[N],num,cnt;bool bridge[N];void addedge(int u,int v,int id) {edge[cnt].id=id;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++; }void tarjan(int u,int in_edge) {dfn[u]=low[u]=++num;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(!dfn[v]){tarjan(v,i);low[u]=min(low[u],low[v]);if(low[v]>dfn[u])ans[edge[i].id]=1;}else if(i!=(in_edge^1))low[u]=min(low[u],dfn[v]);} }struct Edge {int u,v,w,id;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];int find(int x) {return f[x]==x?x:f[x]=find(f[x]); }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++){int j=i+1;while(e[i].w==e[j].w) j++;for(int k=i;k<j;k++){int x=find(e[k].u),y=find(e[k].v);if(x!=y){dfn[x]=dfn[y]=0;head[x]=head[y]=-1;}}for(int k=i;k<j;k++){int x=find(e[k].u),y=find(e[k].v);if(x!=y){addedge(x,y,e[k].id);addedge(y,x,e[k].id);ans[e[k].id]=2;}}for(int k=i;k<j;k++)if(!dfn[find(e[k].u)])tarjan(find(e[k].u),0);for(int k=i;k<j;k++)f[find(e[k].u)]=find(e[k].v);i=j-1;} }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);cnt=2;//initscanf("%d%d",&n,&m);for(int i=1;i<=m;i++)e[i].input(i);Kruskal();for(int i=1;i<=m;i++)cout<<str[ans[i]]<<endl;return 0; }

    樹鏈剖分+線段樹

    // #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/树链剖分+线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。