洛谷P3275 [SCOI2011]糖果(差分约束,最长路,Tarjan,拓扑排序)

洛谷题目传送门

差分约束模板题,等于双向连0边,小于等于单向连0边,小于单向连1边,我太蒻了,总喜欢正边权跑最长路。。。。。。

看遍了讨论版,我是真的不敢再入复杂度有点超级伪的SPFA的坑了

为了保证复杂度,需要缩点后用拓扑排序统计答案。首先全相等的点本质上是相同的,可以缩到一起,所以先来一波Tarjan把0环全缩起来。接着再考虑边权为1的边。如果这时候还出现了环(包括缩点以后的自环),一定是不存在方案的,这是可以用拓扑排序判断。否则,就是个DAG,拓扑排序也可以直接计算出答案。

统计答案要注意:因为缩了点,所以答案要乘上超级点的size;因为每个小朋友都要有糖,所以最后答案+n(或者也可以将虚点的d值初始化为1,只不过最后要减掉1)

我不会说我连Tarjan都不会写调了半个下午的

#include<cstdio>
#include<algorithm>
using namespace std;
#define I inline
#define R register
#define G ch=getchar()
#define REP for(i=1;i<=n;++i)
#define add(L,X,Y)\
    l[++p]=L;to[p]=Y;\
    ne[p]=he[X];he[X]=p;\
    if(!tl[X])tl[X]=p//鬼畜tl指向链表尾部,方便链表合并
const int N=100009,M=N*3;
int p,df,tot,he[N],tl[N],ne[M],to[M],rd[N],d[N],low[N],dfn[N],sz[N],f[N],st[N];
bool l[M];
template<typename T>
I void in(R T&z){
    R char G;
    while(ch<'-')G;
    z=ch&15;G;
    while(ch>'-')z*=10,z+=ch&15,G;
}
void tarjan(R int x){
    low[st[++p]=x]=dfn[x]=++df;
    for(R int y,i=he[x];i;i=ne[i]){
        if(l[i])continue;//只能缩0环
        if(!dfn[y=to[i]]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(!f[y])low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        ++tot;
        do ++sz[f[st[p]]=x];
        while(st[p--]!=x);
    }
}
int main(){
    R int p=0,n,k,S,o,x,y,i,j,cnt=0;
    in(n);in(k);S=n+1;
    R long long ans=n;//好像
    while(k--){
        in(o);in(x);in(y);
        switch(o){
            case 1:add(0,x,y);add(0,y,x);break;
            case 2:add(1,x,y);break;
            case 3:add(0,y,x);break;
            case 4:add(1,y,x);break;
            case 5:add(0,x,y);
        }
    }
    for(i=1;i<=n;++i){add(0,S,i);}//虚点搞上
    tarjan(S);
    for(i=1;i<=S;++i){
        x=f[i];
        for(j=he[i];j;j=ne[j]){
            y=to[j]=f[to[j]];//改一下
            if(x!=y)++rd[y];//在新图上统计入度
            else if(l[j]){puts("-1");return 0;}//自环可以直接判掉
        }
    }
    for(i=1;i<=S;++i)//合并链表
        if(i!=f[i])ne[tl[i]]=he[f[i]],he[f[i]]=he[i];
    st[p=1]=S;
    while(p){
        ++cnt;//统计进入拓扑排序的总点数
        ans+=d[x=st[p--]]*sz[x];
        for(i=he[x];i;i=ne[i]){
            y=to[i];
            d[y]=max(d[y],d[x]+l[i]);
            if(!--rd[y])st[++p]=y;
        }
    }
    printf("%lld\n",cnt<tot?-1:ans);
    return 0;
}

转载于:https://www.cnblogs.com/flashhu/p/8670817.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值