(tarjan建图+topsort+状态压缩) bzoj 2208

2208: [Jsoi2010]连通数

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1489  Solved: 606
[Submit][Status][Discuss]

Description

Input

输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

Output

输出一行一个整数,表示该图的连通数。

Sample Input

3
010
001
100

Sample Output

9

HINT

对于100%的数据,N不超过2000。

Source

第一轮

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<string>
#include<algorithm>
#include<bitset>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
vector<int> e[2005],g[2005];
stack<int> s;
bitset<2005> f[2005];
int n,mp[2005][2005];
int Dfs[2005],low[2005],use[2005],top,newflag,isstack[2005],in[2005],num[2005];
bool vis[2005][2005];
void tarjan(int u)
{
    Dfs[u]=low[u]=++top;
    isstack[u]=1;
    s.push(u);
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        if(!Dfs[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(isstack[v])
            low[u]=min(low[u],Dfs[v]);
    }
    if(low[u]==Dfs[u])
    {
        newflag++;
        int x;
        do
        {
            x=s.top();
            s.pop();
            isstack[x]=0;
            use[x]=newflag;
            num[newflag]++;
        }while(x!=u);
    }
}
void topsort()
{
    queue<int> q;
    for(int i=1;i<=newflag;i++)
    {
        f[i][i]=1;
        if(in[i]==0)
            q.push(i);
    }
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=0;i<g[x].size();i++)
        {
            int v=g[x][i];
            f[v]=f[v]|f[x];
            if(--in[v]==0)
                q.push(v);
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%1d",&mp[i][j]);
            if(i!=j&&mp[i][j])
                e[i].push_back(j);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(!Dfs[i])
            tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<e[i].size();j++)
        {
            int u,v;
            u=i,v=e[i][j];
            if(use[u]!=use[v]&&!vis[use[u]][use[v]])
            {
                g[use[u]].push_back(use[v]);
                vis[use[u]][use[v]]=1;
                in[use[v]]++;
            }
        }
    }
    topsort();
    int ans=0;
    for(int i=1;i<=newflag;i++)
    {
        for(int j=1;j<=newflag;j++)
        {
            if(f[i][j])
                ans+=num[i]*num[j];
        }
    }
    printf("%d\n",ans);
    return 0;
}

  

时间: 2024-10-15 20:17:47

(tarjan建图+topsort+状态压缩) bzoj 2208的相关文章

uva 818(dfs+图+状态压缩)

题意:有n个环,编号从1到n,给出了一些环环相扣的情况,比如给a和b表示a和b两个环的扣在一起的,每个环都是可以打开的,问最少打开多少个环,然后再扣好,可以让所有的环成为一条链. 题解:状态压缩把所有的打开环的情况枚举出来,然后拿去判断是否成立,更新打开环后的图g[i][j],和每个点的度数,不成立有三种情况,1.计算没有打开的环的度数,如果大于2说明不会有链,2.把没有打开环拿去dfs,访问过就vis[i]++,如果vis[i]>=2说明存在环,3.如果打开的环数num + 1小于链的数量,说

BZOJ 1087状态压缩DP

状态压缩DP真心不会写,参考了别人的写法. 先预处理出合理状态, 我们用二进制表示可以放棋子的状态,DP[I][J][K]:表示现在处理到第I行,J:表示第I行的状态,K表示现在为止一共放的棋子数量. #include<stdio.h> #include<iostream> #define N 1111 using namespace std; typedef long long ll; int num,n,m; ll dp[11][1<<11][90]; int hh

BZOJ 2734 集合选数(状态压缩DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2734 题意:给出一个由1到n的数字组成的集合.定义合法子集为若x在子集中则2x.3x均不能在子集中.求有多少个合法的子集. 思路: 1   3    9 2   6    12 4   12   36 对于上面的矩阵,我们发现就等价于不选相邻数字的方案数.因此枚举每个还没有用到的数字,建立以该数字为左上角的矩阵.接着就是状态压缩DP. int a[N][N]; i64 f[2][1<<

「hdu 4845 」拯救大兵瑞恩(状态压缩bfs | 分层图思想)

首先关于分层图思想详见2004的这个论文 https://wenku.baidu.com/view/dc57f205cc175527072208ad.html 这道题可以用状态压缩,我们对于每一把钥匙的状态只有两种,获得了或者没有获得,然后就可以用二进制方法表示,例如一共有5把钥匙,我们如果用二进制数01001表示当前状态,就意味着我们已经拥有了第一类钥匙,第四类钥匙(从右往左看),然后我们就可以把此时的状态压缩为一个int了,节省了很多的空间,具体的操作就用位运算实现. 然后就是简单粗暴的df

poj3592 Instantaneous Transference tarjan缩点+建图

//给一个n*m的地图,坦克从(0 , 0)开始走 //#表示墙不能走,*表示传送门可以传送到指定地方,可以选择也可以选择不传送 //数字表示该格的矿石数, //坦克从(0,0)开始走,只能往右和往下走, //问最多能得到多少矿石 //直接建图,但由于有传送门,需要缩点 //然后用dfs直接搜一条权值最大的路 #include<cstdio> #include<cstring> #include<iostream> #include<vector> #inc

[SDOI2010] 所驼门王的宝藏 [建图+tarjan缩点+DAG dp]

题面传送门: 传送门 思路: 看完题建模,容易得出是求单向图最长路径的问题 那么把这张图缩强联通分量,再在DAG上面DP即可 然而 这道题的建图实际上才是真正的考点 如果对于每一个点都直接连边到它所有的后继节点,那么可以被卡掉(1e5个点在同一行上) 考虑改变思路,运用网络流建图中的一个常用技巧:把横边和竖边映射成点,再从每个点向所在横坐标.纵坐标代表的点连边即可 这样会有2e6+1e5个点,但是tarjan算法效率O(n),完全无压力 自由(和谐)门的话,目前还没有比较好的方法解决 上网看了一

bzoj5017 [Snoi2017]炸弹 (线段树优化建图+)tarjan 缩点+拓扑排序

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5017 题解 这个题目方法挺多的. 线段树优化建图 线段树优化建图的做法应该挺显然的,一个炸弹能够引爆的炸弹的显然应该是一个区间里面的,直接对这个区间进行线段树优化建图. 这样可以得到一个带环图,缩点以后这个炸弹能够炸到的炸弹就是从这个点能够走到的点. 但是这个不太好做,不过可以发现最终的炸弹也是一个区间,所以可以通过缩点后的 DAG 来求出左右端点. 时间复杂度 \(O(n\log n)\)

BZOJ 3218 A+B Problem(最大流 + 主席树优化建图)

题目:A+B Problem 感谢 Nietzsche 在省选紧迫之际花 39' 给我讲这道题. 这题我并没有想出来,感觉又浪费一道好题了. 需要用最小割,建模方式如下(假设若 2 取黑色,1 取白色会使 2 为奇怪方格): 跑一边最大流,求出最小割,用所有的 W + 所有的 B - 最小割,就是答案. 不过,对于每一个结点 2,在寻找像 1 这样(li <= aj <= ri)的结点时,总不能一个一个枚举吧? O(n2) T 飞. 所以,需要用主席树优化一下.线段树优化建图笔记. 代码未完待

【BZOJ 2208】 [Jsoi2010]连通数

2208: [Jsoi2010]连通数 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 1431  Solved: 585 [Submit][Status][Discuss] Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示无边. Output 输出一行一个整数,表示该图的连通数. Sample Input 3 010 001 100 Samp