洛谷 P3275 [SCOI2011] 糖果

题目链接: P3275 [SCOI2011] 糖果

题目大意:

\(N\) 个大于 \(0\) 的数值,其中有 \(M\) 对之间的关系已经给出,请求出所有数字和至少有多大,若无解则输出 \(-1\)
关系有 \(a=b\)\(a>b\)\(a<b\)\(a\geq b\)\(a\leq b\) 五种。
\(N,M\leq 10^5\)

思路:

这道题外表看起来是一个差分约束板子(如果你是卡常大师的话那确实如此),不过出题人没有那么良心,他卡了 \(SPFA\)
由于每个数字 \(\geq 1\) ,可以设 \(d[0]=0\) ,让 \(\forall x,d[x]-d[0]\geq 1\) ,建完图后,考虑这题有什么特殊性质。
连边的时候可以发现所有的边权都是 \(\geq 0\) 的,这意味着如果图中出现一个环,则环上的边权必须全为 \(0\) ,否则必然无解。在这个思路引导下,用 \(Tarjan\) 求出图中的所有强连通分量,而 \(SCC\) 在这里的性质与环相同,若有非零边则无解,否则分量中的所有数都应该相等,这时我们可以将其视为一整块,进行缩点,然后便得到了一张 \(DAG\) ,这样事情就简单了,从节点 \(0\) 所在的 \(SCC\) 出发进行拓扑即可得到每个块的最小数值。
时间复杂度 \(O(N+M)\)

实现细节:

  • \(ans\)\(long\) \(long\)

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#define N 100100
#define M 300100
using namespace std;
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return s*w;
}
int head[N],to[M],nxt[M],len[M];
int H[N],T[M],NX[M],L[M];
int dfn[N],low[N];
int c[N],siz[N],deg[N],dis[N];
int cnt,num,scc;
bool in[N];
stack<int> s;
queue<int> q;
void init(){
    memset(head,-1,sizeof(head));
    memset(H,-1,sizeof(H));
    cnt=-1;
}
void add_e(int a,int b,int l){
    nxt[++cnt]=head[a],head[a]=cnt,to[cnt]=b,len[cnt]=l;
}
void add_c(int a,int b,int l){
    NX[++cnt]=H[a],H[a]=cnt,T[cnt]=b,L[cnt]=l;
}
void tarjan(int x){
    dfn[x]=low[x]=++num;
    s.push(x),in[x]=true;
    for(int i=head[x];~i;i=nxt[i]){
        int y=to[i];
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }else if(in[y])
            low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x]){
        scc++; int y;
        do{
            y=s.top(),s.pop();
            in[y]=false,c[y]=scc;
            siz[scc]++;
        }while(x!=y);
    }
}
void topo(){
    q.push(c[0]);
    while(!q.empty()){
        int cur=q.front(); q.pop();
        for(int i=H[cur];~i;i=NX[i]){
            dis[T[i]]=max(dis[T[i]],dis[cur]+L[i]);
            if(--deg[T[i]]==0)q.push(T[i]);
        }
    }
}
int main(){
    int n,k,x,a,b;
    cin>>n>>k;
    init();
    for(int i=0;i<k;i++){
        x=read(),a=read(),b=read();
        switch(x){
            case 1:add_e(a,b,0),add_e(b,a,0); break;
            case 2:add_e(a,b,1); break;
            case 3:add_e(b,a,0); break;
            case 4:add_e(b,a,1); break;
            case 5:add_e(a,b,0);
        }
    }
    for(int i=1;i<=n;i++)add_e(0,i,1);
    tarjan(0);
    cnt=-1;
    for(int i=0;i<=n;i++){
        for(int j=head[i];~j;j=nxt[j]){
            if(c[i]!=c[to[j]])
                add_c(c[i],c[to[j]],len[j]),deg[c[to[j]]]++;
            else if(len[j])return puts("-1"),0;
        }
    }
    topo();
    long long ans=0;
    for(int i=1;i<=scc;i++)ans+=(long long)siz[i]*dis[i];
    cout<<ans;
    return 0;
}
posted @ 2021-01-29 12:50  Neal_lee  阅读(76)  评论(0编辑  收藏  举报