HDU 6073 Matching In Multiplication(强连通+拓扑)

思路:

先找右边节点入度为1的点,设为b,所以这个点连的边一定做出了贡献。然后找左边连这个点b的点a。
将a所连的点的入度都减减,如果出现入度为1的,那么接着从这个点搜。
直到没有这样的点。
所以剩下的点都在环里。
跑一边tarjan,然后在各自的联通分量内跑一圈,注意:环的贡献由两部分组成,因为是选一条边不选一条边,所以反过来就又有一种。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include <queue>
#include<algorithm>
#define eps 1e-8
typedef long long int lli;
using namespace std;
const int maxn = 650000;
const int mod = 998244353;

struct edge{
    int to,v,next;
}ed[maxn*10];
int head[maxn],cnte;
void ae(int x,int y,int v){
    ed[++cnte].to = y;
    ed[cnte].v = v;
    ed[cnte].next = head[x];
    head[x] = cnte;
}
lli dfn[maxn],low[maxn],stak[maxn],belong[maxn],cntc,cnts,index;
bool vis[maxn];int vc[maxn];
void dfs(lli u,lli f){
    dfn[u] = low[u] = ++index;
    stak[cnts++] = u;
    vis[u] = 1;
    for(lli i = head[u];~i;i=ed[i].next){
        lli v = ed[i].to;
        if(v == f) continue;
        if(!dfn[v]){
            dfs(v,u);
            low[u] = min(low[u],low[v]);
        }
        else if(vis[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u]){
        cntc++;lli v;int cnt =0;
        do{
            cnt++;
            v = stak[--cnts];
            vis[v] = 0;
            belong[v] = cntc;
        }while(v!=u);
        if(cnt == 1) vc[u] = 2;
    }
}
int n;
void tarjan(){
    for(int i = 1;i <= n;i++){
        if(!dfn[i]) dfs(i,-1);
    }
}
bool v[maxn];
void df(int p,lli &ta1,lli &ta2,int fl){
    for(int i = head[p];~i;i=ed[i].next){
        int to = ed[i].to;
        if(v[to]==0 && belong[to]==belong[p]){
            v[to] = 1;
            if(fl == 1)
                ta1 = ta1*(lli)ed[i].v%mod;
            else
                ta2 = ta2*(lli)ed[i].v%mod;
            df(to,ta1,ta2,-fl);
        }
    }
}
int in[maxn];
void ini(){
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(vc,0,sizeof(vc));
    memset(v,0,sizeof(v));
    memset(in,0,sizeof(in));
    cnte = cntc = cnts = index = 0;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        ini();
        scanf("%d",&n);
        int t1,t2,t3,t4;
        for(int i = 1;i <= n;i++){
            scanf("%d%d%d%d",&t1,&t2,&t3,&t4);
            ae(i,n+t1,t2);ae(t1+n,i,t2);
            ae(i,n+t3,t4);ae(t3+n,i,t4);
            in[t1+n]++;in[t3+n]++;
        }
        tarjan();
        lli ans = 1,tans1,tans2;tans1 = 1;
        queue<int> q;int now;
        for(int i = n+1;i <= n+n;i++){
            if(in[i] == 1){
                q.push(i);
            }
        }
        while(!q.empty()){
            now = q.front();q.pop();
            int to,va;
            for(int i = head[now];~i;i=ed[i].next){
                int des = ed[i].to;
                if(!v[des]){
                    to = des,va = ed[i].v;break;
                }
            }
            ans = ans*(lli)va%mod;v[to] = 1;
            for(int i = head[to];~i;i=ed[i].next){
                int v2 = ed[i].to;
                in[v2]--;
                if(in[v2]==1) q.push(v2);
            }
        }
        for(int i = 1;i <= n;i++){
            if(!vc[i]&&!v[i]){
                tans1 = tans2 = 1;
                vc[i] = 1;
                df(i,tans1,tans2,1);
                ans = ans*((tans1+tans2)%mod) % mod;
            }
        }
        printf("%lld\n",(ans%mod+mod)%mod);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值