sgu219:Synchrograph(拓扑)

题目大意:
       在系统学中,Petri nets的一种特殊情况经常被纳入考虑范围,这种特殊情况被称为Synchrograph。Synchrograph是一个有向图,每条弧都有一个非负整数权。一个点,如果所有指向它的边都是正数(也就是大于0),这个点就称之为“可燃点”。
       对Synchrograph的操作是一轮一轮进行的。在每一轮中,操作者都会随机的选一个“可燃点”进行“燃烧”。所谓燃烧就是:所有指向这个点的弧权都减1,所有从这个点指出去的弧都加1。每一轮之后,“可燃点”根据新的弧权被更新,然后下一轮操作继续进行。
       如果存在一个操作序列,使得某个点变成可燃点,那么这个点就称之为“潜在活动点”。
       如果经过任意一个操作序列之后,某个点依然是“潜在活动点”,这个点就称之为“活动点”。

分析:
       看懂样例证明你看懂了题意,而我却看了半天…

       我们考虑非活动点与活动点之间的关系。

       如果一个点 v 的前驱中有非活动点u,那么这个点肯定是非活动点。因为如果它还是潜在活动点,不断燃烧它, (u,v) 的值只会不断减小,最后要么 (u,v)=0 ,要么这个点不是潜在活动点,也就肯定不是活动点了。

       如果一个点的前驱全为活动点,那么这个点肯定为活动点。因为前驱到它的边总是递增的,因此无论怎样它都可以变成潜在活动点。

       如果存在一个边权为 0 的环,那么环上的点一定为非活动点,这个结论很显然。

      除了 0 环上的非活动点外,其他的所有非活动点都是由它们延伸出来的,因此我们先找到0环上的所有点,再由它们扩展出其他非活动点,剩下的就是活动点了。

AC code:

#include <cstdio>
#include <cstring>
#include <queue>
#define ONLINE_JUDGE
using namespace std;

const int MAXN = 1009;
const int MAXM = 50009;

int n, m;
struct Dgraph
{
    int size;
    int head[MAXN];
    int to[MAXM];
    int w[MAXM];
    int ne[MAXM];
    Dgraph(){size = 1;}
    void add_edge(int u, int v, int wi)
    {
        to[size] = v, ne[size] = head[u], w[size] = wi, head[u] = size++;
    }
}G, G2;
int deg[MAXN];
queue<int> q;
int ans[MAXN];

int read()
{
    char c;
    int x = 0, f = 1;
    while(((c=getchar()) < '0' || c > '9') && c != '-');
    if(c == '-') f = -1;
    else x = c-'0';
    while((c=getchar()) >= '0' && c <= '9') x = x*10+c-'0';
    return x*f;
}


int main()
{
    #ifndef ONLINE_JUDGE
    freopen("sgu219.in", "r", stdin);
    freopen("sgu219.out", "w", stdout);
    #endif

    n = read(), m = read();
    for(int i = 1; i <= m; ++i)
    {
        int u = read(), v = read(), w = read();
        G.add_edge(u, v, w), deg[v] += w==0;
    }
    for(int i = 1; i <= n; ++i)
        if(!deg[i]) q.push(i);
    while(!q.empty())
    {
        int u = q.front();q.pop();
        for(int i = G.head[u]; i; i = G.ne[i])
            if(!G.w[i])
                if(!(--deg[G.to[i]]))
                    q.push(G.to[i]);
    }
    for(int i = 1; i <= n; ++i) ans[i] = 1;
    for(int i = 1; i <= n; ++i) 
        if(deg[i]) ans[i] = 0, q.push(i);
    while(!q.empty())
    {
        int u = q.front();q.pop();
        for(int i = G.head[u]; i; i = G.ne[i])
            if(ans[G.to[i]])
                ans[G.to[i]] = 0, q.push(G.to[i]);
    }
    for(int i = 1; i <= n; ++i)
        printf("%d\n", ans[i]);

    #ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    #endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值