zoukankan      html  css  js  c++  java
  • JSOI2010 联通数

    传送门

    这道题的题目描述看起来很奇怪。实际上的意思是要求在这个有向图之内能到达的点对有多少,解释一下题里的图片就是(1,1),(1,2),(1,3),(1,4),(1,5),(2,2),(2,3),(2,4),(2,5),(3,3),(3,4),(3,5),(4,4),(5,5)一共14个。

    先小声说一下这题固输n^2可以得到90pts……

    然后我们首先考虑非常暴力的做法,就是先手tarjan缩点,存每个联通块里面的节点个数,在新图上直接进行dfs,把每个点所能到达的点搜出来就行。

    我也不知道能跑多快……但是如上文所述你固输都有90所以数据比较水,我一共跑了50ms就过了。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<bitset>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 2005;
    const int N = 1000005;
    const double eps = 1e-6;
    const double fi = 0.61803399;
    const double fim = 0.38196601;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    struct edge
    {
        int next,to,from;
    }e[N<<2],e1[N<<2];
    int n,g[M][M],dfn[M],low[M],stack[M],top,idx,cur,ecnt,head[M],sd[M],h[M],sz[M],ecnt1,q[M],curr,tot;
    char s[M];
    ll ans;
    bool vis[M],pd[M];
    
    void add(int x,int y)
    {
        e[++ecnt].to = y;
        e[ecnt].from = x;
        e[ecnt].next = head[x];
        head[x] = ecnt;
    }
    
    void tarjan(int x)
    {
        low[x] = dfn[x] = ++idx;
        vis[x] = 1,stack[++top] = x;
        for(int i = head[x];i;i = e[i].next)
        {
        if(!dfn[e[i].to]) tarjan(e[i].to),low[x] = min(low[x],low[e[i].to]);
        else if(vis[e[i].to]) low[x] = min(low[x],low[e[i].to]);
        }
        if(low[x] == dfn[x])
        {
        int p;
        cur++;
        while(p = stack[top--])
        {
            sd[p] = cur,vis[x] = 0,sz[cur]++;
            if(x == p) break;
        }
        }
    }
    
    void dfs(int x)
    {
    
        tot += sz[x],pd[x] = 1,q[++curr] = x;
        for(int i = h[x];i;i = e1[i].next)
        {
        if(pd[e1[i].to]) continue;
        dfs(e1[i].to);
        }
    }
    
    int main()
    {
        n = read();
        rep(i,1,n)
        {
        scanf("%s",s);
        rep(j,0,n-1) if(s[j] == '1') add(i,j+1);
        }
        rep(i,1,n) if(!dfn[i]) tarjan(i);
        rep(i,1,ecnt)
        {
        int r1 = sd[e[i].from],r2 = sd[e[i].to];
        if(r1 != r2)
        {
            e1[++ecnt1].to = r2;
            e1[ecnt1].next = h[r1];
            e1[ecnt1].from = r1;
            h[r1] = ecnt1;
        }
        }
        rep(i,1,cur)
        {
        rep(i,1,curr) q[i] = 0;tot = 0;
        dfs(i);
        rep(i,1,curr) pd[q[i]] = 0;
        ans += tot * sz[i];
        }
        printf("%lld
    ",ans);
        return 0;
    }

    这样很神奇你的搜索就过了……

    之后我们再说一种别的做法,他有一个高大上的名字,叫Floyd传递闭包!

    这个东西可以判断有向图两点之间是否连通,一开始它的形状和初始给定的邻接矩阵是一样的,不过大对角线上的点都变成了1.

    正常的dp是三重循环枚举,dp[i][j] = dp[i][k] & dp[k][j]。不过这样枚举O(n^3)会T,然后我们发现只有能和不能到两种状态,所以我们引入一种强大的东西——bitset来帮我们优化。

    使用bitset压缩一行的状态,这样的话转移方程就变成了dp[i] |= dp[j].这样我们只需要O(n^3/32)的复杂度,就可以过了。

    看一下代码(炒鸡短)

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<bitset>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 5005;
    const double eps = 1e-6;
    const double fi = 0.61803399;
    const double fim = 0.38196601;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    bitset <2005> f[M];
    int n;
    char s[M];
    ll ans;
    
    int main()
    {
        n = read();
        rep(i,1,n)
        {
        scanf("%s",s);
        rep(j,0,n-1) if(s[j] == '1') f[i][j+1] = 1;
        f[i][i] = 1;
        }
        rep(i,1,n)
        rep(j,1,n) if(f[j][i]) f[j] |= f[i];
        rep(i,1,n) ans += f[i].count();
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    linux下安装jdk1.8
    在eclipse里面启动tomcat服务器时报错Server Tomcat v7.0 Server at localhost failed to start.
    frame使关闭口生效
    web.xml配置错误处理节点
    mac下修改mysql默认字符集为utf8
    Centos6.5关闭防火墙命令
    mysql修改
    java错误:The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build
    完美解决Linux服务器tomcat开机自启动问题
    MAC 查看java home目录
  • 原文地址:https://www.cnblogs.com/captain1/p/9681021.html
Copyright © 2011-2022 走看看