基环树

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/81711872

基环树也叫环套树(明明更像树套环)
就是n个点n条边的连通图,可以发现只有一个环,并且删掉环上任意一个边可以变成一棵树。
尽管许多题解上都会说什么把环求出来然后分类讨论什么的,
但是,实际上删掉环上的一条边然后再加回去分类讨论要好做的多。。。。。
毕竟你不需要树上一个数据结构,再在环上再开一个数据结构。。。。。。。
套路了。

例1:HDU 6403 Card Game,简单的分类讨论基环树DP

题意
给定 n 张卡片,每张卡片正反面各有一个数。问至少要翻转多少张卡片,才能使正面向上的数互
不相同,并求方案数。
题解
首先建图:每个数字为一个节点,每张卡片反面数字向正面数字连一条有向边。问题转化为:至
少要反转多少条边的方向,才能使得每个点的入度不会超过 1。我们对每个弱连通分量分别处理。易
知,当底图是树或基环树时,才可能有解。对于基环树,先把环找出来,然后将环上的边的方向统一
一下;非环边的方向则是唯一确定的,从环上的点向外做一遍 dfs 即可。对于树,可以正反两次 dfs
处理出每个点作为根时所需要的反向次数,并统计出最小值以及方案数。最后将答案合并即可。

AC Code :

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 200005
#define mod 998244353
using namespace std;

inline void read(int &res){ char ch;for(;!isdigit(ch=getchar()););for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); }
int n,info[maxn],Prev[maxn],to[maxn],dir[maxn],cnt_e;
bool vis[maxn];
inline void Node(const int &u,const int &v,const int &typ){Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,dir[cnt_e]=typ;}

int cnte=0,cntp=0;
bool ERROR=0;

void check(int now,int ff)
{
    cntp++;
    vis[now] = 1;
    for(int i=info[now];i;i=Prev[i])
    {
        cnte++;
        if(!vis[to[i]]) check(to[i],now);
    }
}

int st=-1,ed=-1,id=-1;
int dp[maxn],dp2[maxn],sta[maxn],tp;

void dfs(int now,int ff)
{
    vis[now] = 1;
    dp[now] = 0;
    for(int i=info[now];i;i=Prev[i])
        if(!vis[to[i]])
        {
            dfs(to[i],now);
            dp[now]+=dp[to[i]]+(!dir[i]);
        }
        else if(to[i]!=ff){ st=now , ed = to[i] , id = i;}
}

int ans = 0 , Min , ways , tway;
void ser(int now,int ff)
{
    sta[tp++] = now;
    Min = min(Min , dp2[now]);
    for(int i=info[now];i;i=Prev[i])
        if(i!=ff && (i^1)!=ff && i!=id && i!=(id^1))
        {
            dp2[to[i]] = dp2[now] + (dir[i] ? 1 : -1);
            ser(to[i],i);
        }
}

int main()
{

    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    #endif

    int T,u,v;
    for(read(T);T--;)
    {
        cnt_e=ways=1,ERROR=ans=0;
        memset(info,0,sizeof info);
        memset(vis,0,sizeof vis);
        read(n);
        for(int i=1;i<=n;i++)
        {
            read(u),read(v);
            Node(v,u,1),Node(u,v,0);
        }
        for(int i=1;i<=2*n;i++)
            if(!vis[i])
            {
                cntp=cnte=0;
                check(i,0);
                if(cnte/2 > cntp){ ERROR=1;break; }
            }
        if(ERROR){ puts("-1 -1");continue; }

        memset(vis,0,sizeof vis);
        for(int i=1;i<=2*n;i++)
            if(!vis[i])
            {
                st=ed=id=-1;
                dfs(i,0);

                Min = 0x3f3f3f3f , tway = tp = 0 , dp2[i] = dp[i];
                ser(i,0);
                if(st == -1){for(int j=0;j<tp;j++) if(dp2[sta[j]]==Min) tway++;}
                else Min = min(dp2[st] + dir[id] , dp2[ed] + (!dir[id])),tway=(dp2[st] + dir[id] == dp2[ed] + (!dir[id]) ? 2 : 1);
                ans += Min , ways = 1ll * ways * tway % mod;
            }

        printf("%d %d\n",ans , ways);
    }

}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/81711872