5064. 友好城市

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

题目大意

给定 n 个点m条有向边。
q 个询问,每个询问要求只用编号为[li,ri]的边有多少对点可以相互到达。

Data Constraint
n150,m300000,q50000

题解

对于一个询问,如果求出所有的强连通分量大小的话,问题就变得很简单了。
用Kosaraju算法求强连通分量,然后就可以很方便的用bitset保存边集优化转移。
对于原边集可以先分块,然后维护一个边集的ST表。每个询问就可以很方便的取出对应的边集了。

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<cmath>
using namespace std ;

#define N 300000 + 10
#define M 250 + 10
#define K 650 + 10
const int MAXN = 11 ;
struct Edge {
    int u , v ;
} E[N] ;

bitset < M > vis , tp ;
bitset < M > ret[M] , trans[M] , retrans[M] ;
bitset < M > f[2][K][MAXN][M] ;

int S[M] , Tab[N] ;
int Belong[N] ;
int n , m , Q ;
int Size , Cnt , Block ;

void Merge( int type , int i1 , int i2 , int j ) {
    for (int x = 1 ; x <= n ; x ++ ) {
        if ( i2 > Cnt ) ret[x] = f[type][i1][j][x] ;
        else ret[x] = f[type][i1][j][x] | f[type][i2][j][x] ;
    }
}

void Get( int l , int r ) {
    int k = Tab[r-l+1] ;
    Merge( 0 , l , r - (1 << k) + 1 , k ) ;
    for (int x = 1 ; x <= n ; x ++ ) trans[x] = ret[x] ;
    Merge( 1 , l , r - (1 << k) + 1 , k ) ;
    for (int x = 1 ; x <= n ; x ++ ) retrans[x] = ret[x] ;
}

void DFS( int x ) {
    vis[x] = 0 ;
    while ( 1 ) {
        tp = trans[x] & vis ;
        if ( !tp.any() ) break ;
        int Son = tp._Find_next(-1) ;
        DFS( Son ) ;
    }
    S[++S[0]] = x ;
}

void ReDFS( int x ) {
    vis[x] = 0 ;
    Size ++ ;
    while ( 1 ) {
        tp = retrans[x] & vis ;
        if ( !tp.any() ) break ;
        int Son = tp._Find_next(-1) ;
        ReDFS( Son ) ;
    }
}

int main() {
    freopen( "friend.in" , "r" , stdin ) ;
    freopen( "friend.out" , "w" , stdout ) ;
    scanf( "%d%d%d" , &n , &m , &Q ) ;
    Block = sqrt(m) ;
    for (int i = 1 ; i <= m ; i ++ ) {
        Belong[i] = i / Block + ( i % Block != 0 ) ;
        scanf( "%d%d" , &E[i].u , &E[i].v ) ;
    }
    Cnt = Belong[m] ;
    Tab[1] = 0 ;
    for (int i = 2 ; i <= Cnt ; i ++ ) Tab[i] = Tab[i/2] + 1 ;
    for (int i = 1 ; i <= Cnt ; i ++ ) {
        for (int j = (i - 1) * Block + 1 ; j <= i * Block ; j ++ ) {
            f[0][i][0][E[j].u][E[j].v] = 1 ;
            f[1][i][0][E[j].v][E[j].u] = 1 ;
        }
    }
    for (int type = 0 ; type <= 1 ; type ++ ) {
        for (int j = 1 ; j < MAXN ; j ++ ) {
            for (int i = 1 ; i <= Cnt ; i ++ ) {
                Merge( type , i , i + (1 << (j - 1)) , j - 1 ) ;
                for (int x = 1 ; x <= n ; x ++ ) f[type][i][j][x] = ret[x] ;
            }
        }
    }
    for (int i = 1 ; i <= Q ; i ++ ) {
        int l , r ;
        scanf( "%d%d" , &l , &r ) ;
        if ( Belong[l] != Belong[r] ) {
            for (int x = 1 ; x <= n ; x ++ ) trans[x].reset() , retrans[x].reset() ;
            int lx = Belong[l-1] != Belong[l] ? Belong[l] : Belong[l] + 1 ;
            int rx = Belong[r+1] != Belong[r] ? Belong[r] : Belong[r] - 1 ;
            if ( lx <= rx ) Get( lx , rx ) ;
            for (int j = l ; j <= (lx - 1) * Block ; j ++ ) {
                trans[E[j].u][E[j].v] = 1 ;
                retrans[E[j].v][E[j].u] = 1 ;
            }
            for (int j = rx * Block + 1 ; j <= r ; j ++ ) {
                trans[E[j].u][E[j].v] = 1 ;
                retrans[E[j].v][E[j].u] = 1 ;
            }
        } else {
            for (int x = 1 ; x <= n ; x ++ ) trans[x].reset() , retrans[x].reset() ;
            for (int j = l ; j <= r ; j ++ ) {
                trans[E[j].u][E[j].v] = 1 ;
                retrans[E[j].v][E[j].u] = 1 ;
            }
        }
        S[0] = 0 ;
        vis.set() ;
        for (int x = 1 ; x <= n ; x ++ ) {
            if ( vis[x] == 0 ) continue ;
            DFS(x) ;
        }
        vis.set() ;
        int ans = 0 ;
        while ( S[0] ) {
            if ( vis[S[S[0]]] == 0 ) { S[0] -- ; continue ; }
            Size = 0 ;
            ReDFS( S[S[0]] ) ;
            ans += Size * (Size - 1) / 2 ;
            S[0] -- ;
        }
        printf( "%d\n" , ans ) ;
    }
    return 0 ;
}

以上.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值