HDU 5304 Eastest Magical Day Seep Group's Summer

5 篇文章 0 订阅
2 篇文章 0 订阅

Eastest Magical Day Seep Group's Summer

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 191    Accepted Submission(s): 49


Problem Description
As we know, Tsuyuri Kumin likes sleeping in Eastest magical day sleep group's summer. But Rikka wants Kumin to play games with her. So she comes up with one problem:

Here is an undirected graph G with n vertices and m edges. Now you need to delete mn edges and to make sure that the remain graph is connected. Rikka wants you to tell her the number of ways to choose the edges.

Kumin wants to go to sleep, so she asks you to answer this question. Can you help her?
 

Input
There are at most 100 testcases,and there are no more 5 testcases with n10 .

For each test case, the first line contains two integers n,m (1n16,nmn(n1)2) .

Then m lines follows. Each of them contains two integers ui,vi , meaning that there is an edge between ui and vi . It is guaranteed that the graph doesn't contain self loops or multiple edges.
 

Output
For each testcase print a single integer - the number of ways to choose the edges. The answer may be very large, so you only need to print the answer modulo 998244353.
 

Sample Input
  
  
4 5 1 2 2 3 3 4 4 1 1 3
 

Sample Output
  
  
5
 

题目大意:

给你一个图,求这个图的基环外向树。


解题思路:

如果本题的要求仅仅只是求这个图的生成树的个数,那么可以使用Kirchhoff矩阵求解:

Kirchhoff矩阵定义如下:

1、G的度数矩阵D[G]是一个N*N的矩阵,并且满足D[i][i] = 节点i的度数;D[i][j] = 0;

2、G的邻接矩阵A[G]是一个N*N的矩阵,并且满足A[i][j] = i 和 j之间的边的数量

3、G的Kirchhoff矩阵C[G]是一个N*N的矩阵,并且满足C = D - A

那么,该图的生成树的数量就是C[G]的任意主子式的行列式的值的绝对值。

但是该题中还存在一个问题,就是要求树上存在一个环,那么就需要缩点来实现了。将符合要求的环缩成一个点,最后的结果就是Σ(circle[i] * |C[G]|)。

先在仅剩下求环问题了。由于会缩点,因此,对同样的点的环,我们并不关心其环的形状,我们关心的仅仅是环的数量,因此可以将点进行状压(点的个数只有16个)。有如下定义:

dp[i][j] 代表 i 中状态下呈现链状的最后节点为j的种类

dp[i][j] = Σ dp[k][p](实际中是对dp[k][p]向后进行转移的)

同时保证,i状态下,链的起点为i中编号最小的点。

通过sum[i]数组记录i状态下的方案数。

通过上述步骤就可以解决该题了。


Tips:

由于缩点所产生的重复边是需要保存的。

由于环存在方向性,因此,就算规定了起点,对于一条链也会有2种转移方法,因此,最后结果需要除2。

在本题中与要注意使用逆元实现最后的除2,不能直接去除


源代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long LL;
#define rt return
#define MOD 998244353
#define eps 1e-3
#define N 30
LL n,m,x,y;
LL mapt[30][30];
LL maptt[30][30];
LL dp[100000][30];
LL tt[30];
LL sum[100000];
const LL d[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
LL multi(LL a,LL b)
{
    LL tot = 1;
    while(b)
    {
        if(b&1) tot=(tot*a)%MOD;
        a=(a*a)%MOD;
        b/=2;
    }
    return tot;
}
int sig(long double x){return (x>eps)-(x<-eps);}
struct Mat{
    LL n,m;
    long double mat[N][N];
    LL b[N];
    void clear(){memset(mat,0,sizeof mat);n = 0;m=0;}
    void output(){for(int i =0;i<n;i++){for(int j = 0;j<m;j++)cout<<mat[i][j]<<" ";cout<<endl;}}
    void setMat(LL n,LL m,LL d[30][30])
    {
        this->n = n;
        this->m = m;
        for(LL i = 0;i<n;i++)
            for(LL j = 0;j<m;j++)
                mat[i][j] = d[i][j];
    }
    long double det(LL n)//行列式求值
    {
        for(LL i = 0;i<n;i++) b[i] = i;
        for(LL k=0;k<n;k++)
        {
            LL v = k;
            long double maxx = 0.0;
            for(LL i = k+1;i<n;i++)
                if(fabs(mat[b[i]][k])>maxx){maxx=fabs(mat[b[i]][k]);v= i;}
            if(sig(maxx)==0) continue;
            swap(b[k],b[v]);
            v = b[k];
            for(LL i=k+1;i<n;i++)
            {
                LL u = b[i];
                long double f=mat[u][k] / mat[v][k];
                for(LL j = k+1;j<n;j++)mat[u][j]-=f*mat[v][j];
            }
        }
        long double ans = 1;
        for(LL i = 0;i<n;i++) ans*=mat[b[i]][i];
        return fabs(ans);
    }
}a;
LL getCount(LL k){LL cnt = 0;while(k) {cnt+=k&1;k/=2;}rt cnt;}
LL pos(LL k){LL cnt = 1;while(k%2==0){cnt++;k/=2;} rt cnt;}
void input()
{
    memset(mapt,0,sizeof mapt);
    for(LL i = 0;i<m;i++)
         {
             scanf("%lld%lld",&x,&y);
             mapt[x][y]++;
             mapt[y][x]++;
         }
}
inline void calcCircles()
{
    memset(dp,0,sizeof dp);
    memset(sum,0,sizeof sum);
    LL tot = 0;
    for(LL i = 1;i<d[n];i++)
    {
        LL cnt = getCount(i);
        LL p = pos(i);
        if(cnt == 1) dp[i][p] = 1;
            for(LL j = 1;j<=n;j++)
            if(i&d[j-1])
            {
                for(LL l=p+1;l<=n;l++)
                    if(mapt[j][l] && (d[l-1]&i) == 0)
                        dp[i+d[l-1]][l]=(dp[i+d[l-1]][l]+dp[i][j])%MOD;
                if(cnt>=3 && mapt[j][p])
                    sum[i]=(sum[i]+dp[i][j])%MOD;
            }
    }
}
inline void solve()
{
    LL ans;
    ans = 0;
    for(LL i = 1;i<d[n];i++)
    if(sum[i])
        {
            LL cnt = getCount(i);
            a.clear();
            memset(maptt,0,sizeof maptt);
            memset(tt,0,sizeof tt);
            LL p = 0;
            for(LL j =1;j<=n;j++)
                if(i&d[j-1])tt[j] = n-cnt;
                else tt[j] = p++;
            for(LL j = 1;j<=n;j++)
                for(LL l=1;l<=n;l++)
                        if(tt[j]!=tt[l] && mapt[j][l])
                            {
                                maptt[tt[j]][tt[l]]--;
                                maptt[tt[j]][tt[j]]++;
                            }
            a.setMat(n-cnt+1,n-cnt+1,maptt);
            long double t = a.det(n-cnt);
            LL tmp = (LL)(t + eps ) % MOD;
            ans = (ans + sum[i] * tmp)%MOD;
        }
        ans  = (ans * multi(2,MOD-2))%MOD;
        printf("%lld\n",ans);
}
int main()
{
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
         input();
         calcCircles();
         solve();
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,HDU1622是一道关于二叉树的题目,要求读入一系列二叉树的节点信息,输出它们的层序遍历结果。如果输入的二叉树不完整或存在重复节点,则输出"not complete"。下面是Java的实现代码: ```java import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; public class Main { static class Node { int val; Node left, right; public Node(int val) { this.val = val; } } public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNext()) { String s = sc.nextLine(); if (s.isEmpty()) { continue; } String[] nodes = s.split("\\s+"); Node root = new Node(Integer.parseInt(nodes[0].substring(1))); Queue<Node> queue = new LinkedList<>(); queue.offer(root); boolean isComplete = true; for (int i = 1; i < nodes.length - 1; i += 2) { Node cur = queue.poll(); if (!nodes[i].equals("()")) { cur.left = new Node(Integer.parseInt(nodes[i].substring(1))); queue.offer(cur.left); } else { isComplete = false; } if (!nodes[i + 1].equals("()")) { cur.right = new Node(Integer.parseInt(nodes[i + 1].substring(0, nodes[i + 1].length() - 1))); queue.offer(cur.right); } else { isComplete = false; } } if (!isComplete) { System.out.println("not complete"); continue; } StringBuilder sb = new StringBuilder(); queue.offer(root); while (!queue.isEmpty()) { Node cur = queue.poll(); sb.append(cur.val).append(" "); if (cur.left != null) { queue.offer(cur.left); } if (cur.right != null) { queue.offer(cur.right); } } System.out.println(sb.toString().trim()); } } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值