图论进阶指南-银河(差分约束/DAG/tarjan)

测评地址

题目大意:

第一行给出两个整数N和M。
之后M行,每行三个整数T,A,B,表示一对恒星(A,B)之间的亮度关系。恒星的编号从1开始。
如果T=1,说明A和B亮度相等。
如果T=2,说明A的亮度小于B的亮度。
如果T=3,说明A的亮度不小于B的亮度。
如果T=4,说明A的亮度大于B的亮度。
如果T=5,说明A的亮度不大于B的亮度。
就是告诉你点之间的关系,给每个点确定边权使总和最小最小最小最小

在差分约束系统中有两种表达式

1
x[j] >= x[i] + K 
在松弛后所有的点都满足以上条件
所以这个表达式建图的话是跑最长路的
每个点的上界是无穷的
下界可以通过最长路求出来
所以所有点权总和最小值 可以 用表达式1建图跑最长路求
2
x[j] <= x[i] + K 
跟1式类似

这个题是存在
就比如 val[1] = val[2] ,val[2]=val[3] ,val[3]=val[1]
那么1 ,2 ,3 三个点就形成了一个环
环中的任意两点间不能存在大于0的边权
如果存在的话,求最长路会死循环(无解的情况)

那么每个联通块对答案的贡献就是(size×w)

接下来我们把每一个联通块看作一个单独的点在DAG上跑一边最长路就ok了

具体操作

1.tarjan 缩点(假设都会建图)
2, 建图的时候建超级原点
(也可以在缩完点之后建新图的时候在建立超级原点)
3.每个点的点权最少为1
(因为有超级原点的存在,这里可以不用初始化)
4.DAG上dp求最长路
5.求解答案(每个联通块的贡献)

CODE:

#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
const int maxn = 4e5 + 70;
int  head[maxn],cntt;
struct node {
     
    int  u,v,w,next;
} e[maxn];
void add(int u,int v,int w) {
     
    e[cntt].u=u,e[cntt].v=v,e[cntt].w=w;
    e[cntt].next=head[u],head[u]=cntt++;
}
int n,m,dfn[maxn],indexx,low[maxn],vis[maxn],top,st[maxn];
int cnt,id[maxn],in[maxn],dp[maxn];;
vector<int>ne[maxn];
vector< pair<int,int> > g[maxn];
void tarjan(int u) {
     
    dfn[u] = low[u] = ++indexx;
    vis[u] =1,st[++top] = u;
    for(int i=head[u]; ~i; i=e[i].next) {
     
        int v = e[i].v;
        if(!dfn[v]) tarjan(v),low[u] =  min(low[u],low[v]);
        else if(vis[v]) low[u] =  min(low[u],low[v]);
    }
    if(dfn[u]==low[u]) {
     
        int yy;
        ++cnt;
        do {
     
            yy=st[top];
            vis[yy] =0 ;
            id[yy]=cnt;
            ne[cnt].push_back(yy);
            top--;
        } while(yy!=u);
    }
}
void DAG() {
     
    queue<int>q;
    for(int i=1; i<=cnt; i++) if(in[i]==0) q.push(i),dp[i] = 0;
    while(q.size()) {
     
        int fr = q.front();
        q.pop();
        for(auto t:g[fr]) {
     
            int v = t.first;
            int w = t.second;
            dp[v] = max(dp[v],dp[fr]+w);
            in[v]--;
            if(in[v]==0) q.push(v);
        }
    }
    ll temp=0;
    for(int i=1 ; i<=cnt ; i++) temp+=ne[i].size()*dp[i]*1ll;
    cout<<temp;
}
int main() {
     
    //  ios::sync_with_stdio(false);
    cin>>n>>m;
    memset(head,-1,sizeof head);
    for(int i=1 ; i<=n ; i++) add(0,i,1);
    for(int i=1 ; i<=m ; i++) {
     
        int op,u,v;
        cin>>op>>u>>v;
        if(op==1)  add(u,v,0),add(v,u,0);
        else if(op==2) add(u,v,1);
        else if(op==3) add(v,u,0);
        else if(op==4) add(v,u,1);
        else if(op==5) add(u,v,0);
    }

    for(int i=0 ; i<=n ; i++) if(dfn[i]==0)tarjan(i);

    int flag=0;
    for(int i=0 ; i<=n ; i++) {
     
        if(flag) break;
        for(int j=head[i]; ~j; j=e[j].next) {
     
            int v = e[j].v;
            if(id[v]==id[i]) {
     
                if(e[j].w>0) {
     
                    flag=1;
                    break;
                }
            } else {
     
                g[id[i]].push_back({
     id[v],e[j].w});
                in[id[v]]++;
            }
        }
    }
    if(flag) cout<<"-1";
    else DAG();
    return 0;
}
/*

*/

你可能感兴趣的:(训练,图论)