图论专题M题
题意主要是:给出两个值大小关系,每个值至少为1,求出值的总和最少为多少,如果有矛盾输出-1
这道题之所以记下来,主要是处理图的方式是比较经典的,我觉得值得参考。
我一开始想到spfa,又因为这是多源的,所以采用了拓扑排序求最长路。
用并查集维护相等关系,最后对每个值取同一集合中最大值。
如果存在环,说明出现矛盾。
但是相等关系出现矛盾怎么维护呢?这个就是关键点了,对于1->3->5,1<3<5,如果5<1会构成环自然不对。但是1和5相等也是不对的,这个怎么判断呢,我们可以尝试让1和5连在一起也变成环,处理方式就是并查集缩点。
最后计算答案find找到祖先即可。
并查集缩点是一个比较偏僻但是关键的知识点,需要好好记住。处理出现矛盾的相等关系很有用。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn=40200;
const int maxm=300050;
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++;}
int ind[maxn];
ll d[maxn];
queue<int>q;
bool topo() {
for (int i = 1; i <= n; i++) {
d[i]=1;
if (!ind[i])q.push(i);
}
int cnt=0;
while (!q.empty()) {
cnt++;
int now = q.front();q.pop();
for (int i = he[now]; i; i = ne[i]) {
Edge &e = ed[i];
d[e.to] = max(d[e.to], d[now] + 1);
if (!--ind[e.to]) q.push(e.to);
}
}
if(cnt==n)return true;
else return false;
}
int f[maxn];
int find(int x){return f[x]==x?x:(f[x]=find(f[x]));}
bool query(int x,int y){return find(x)==find(y);}
void uni(int x,int y){f[find(x)]=find(y);}
int mark[maxn],A[maxn],B[maxn];
int main(){
cin>>n>>m;
FOR(i,1,n)f[i]=i;
bool ok = true;
memset(ind,0,sizeof(ind));
memset(d,0,sizeof(d));
FOR(i,1,m){
scanf("%d%d%d",&mark[i],&A[i],&B[i]);
if(mark[i]==3){
if(!query(A[i],B[i])){
uni(A[i],B[i]);
}
}
}
FOR(i,1,m){
if(mark[i]!=3){
int x=find(A[i]);
int y=find(B[i]);
if(mark[i]==1)insert(y,x,1),ind[x]++;
if(mark[i]==2)insert(x,y,1),ind[y]++;
}
}
if(topo()){
ll ans=0;
FOR(i,1,n){
ans+=d[find(i)];
}
cout<<ans<<endl;
}
else puts("-1");
}