图论A题、B题

就是两道很巧妙的题,记录在这里。
A题
反转边,使得任意一点总能走到底(随机走)。
但是坏的情况是,无限走环。
所以我们需要反转边使得无环,同时价值等于最大边,要求价值尽可能的小。
有价值是最大边的限制,我们很容易想到二分。
对于环的剔除,只要修改环的一条边即可,但是这样可能造成新的环。
我们反过来思考,对于一个无环图,怎么加边使得依旧无环呢。
无环图的拓扑排序是合法的,不存在从拓扑排序后面的指向前面的,所以对于无环图,我们把边永远从前面指向后面,同一拓扑序的随意。显然能满足情况。
二分最大权,去除权值以下的判断是否无环,无环则满足条件。

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

#define ll long long
#define eps 1e-6
#define inf 0x3f3f3f3f3f3f3f3f
const int maxn=205000;
const int maxm=2000000;

int n,m;

struct Edge{
    int from,to,dist;
    Edge(){}
    Edge(int _from,int _to,int _dist):from(_from),to(_to),dist(_dist){}
};
Edge ed[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,int w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}

vector<int>G[maxn];

int ind[maxn];
bool topo(){
    int num=0;
    queue<int>q;
    for(int i=1;i<=n;i++){
        if(!ind[i])q.push(i);
    }
    while(!q.empty()){
        int now=q.front();q.pop();
        num++;
        for(int i=0;i<G[now].size();i++){
            int to=G[now][i];
            if(!--ind[to])q.push(to);
        }
    }
    if(num<n)return false;
    return true;
}

bool check(int x){
    memset(ind,0,sizeof(ind));
    for(int i=1;i<=n;i++)G[i].clear();
    for(int i=1;i<etop;i++){
        Edge& e=ed[i];
        if(e.dist>x){
            G[e.from].push_back(e.to);
            ind[e.to]++;
        }
    }
    return topo();
}

int main(){
    cin>>n>>m;
    FOR(i,1,m){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        insert(x,y,z);
    }
    int l=0,r=(int)2e9,ans;
    while(l<=r){
        //cout<<l<<" "<<r<<endl;
        int mid=l+(r-l)/2;
        if(check(mid)){
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    cout<<ans<<endl;
}

第二题是如果能免费一条边,求生成树的最小花费,和实现最小花费的免费边数。
就是开头选择免费一条边,能够通过一个生成树实现最小花费
首先呢,最小生成树去掉最大边一定是最小花费,不可能再小了。
我们考虑的一个问题就是,我们用其他边替换最大边,这样免费了同样的效果。
加边是一定形成环的,如果加的边权大于等于最大边,且最大边在环上,那么这条边就是合法的。其实前面的条件可以不要,如果边权小于的话,最小生成树一定包括的是这条边了(去除原来的最大边)
同时最小生成树上的最大边本身也是答案,可能会有多个。
我们利用并查集,合并所有小于最大边的最小生成树的边。这样子判断的时候如果不连通,不是树上最大边,就是合法的树外边(不连通说明加的环上有最大边)。

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

#define ll long long
#define eps 1e-6
#define inf 0x3f3f3f3f3f3f3f3f
const int maxn=200050;
const int maxm=400050;

int n,m;
struct Edge{
    int from,to,dist;
    Edge(){}
    Edge(int _from,int _to,int _dist):from(_from),to(_to),dist(_dist){}
    friend bool operator < (Edge a,Edge b){return a.dist<b.dist;}};
Edge ed[maxm],edges[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,int w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}

int f[maxn],flag[maxm];
int ans=-1;
long long all=0;
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;
    for(int i=1;i<etop;i++)edges[i]=ed[i];
    sort(edges+1,edges+etop);
    for(int i=1;i<etop;i++){
        Edge &e = edges[i];
        if(find(e.from)==find(e.to))continue;
        else{
            all+=(long long)(e.dist);
            ans=max(ans,e.dist);
            flag[i]=1;
            f[find(e.from)]=find(e.to);
        }
    }
}

int res=0;

void slove(){
    kruskal();
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<etop;i++)if(edges[i].dist<ans){
        f[find(edges[i].from)]=find(edges[i].to);
    }
    for(int i=1;i<etop;i++){
        if(find(ed[i].from)!=find(ed[i].to)){
            res++;
            i++;
        }
    }
    cout<<all-(ll)ans<<" "<<res<<endl;
}

int main(){
    cin>>n>>m;
    FOR(i,1,m){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        insert(x,y,z);
        insert(y,x,z);
    }
    slove();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值