牛客练习赛77-E-小G的GLS图(建图优化,拉点,割点)

题目大意:

给你 n n n个点,每个点有一个点权 a i a_i ai。两个点 i , j i,j i,j之间有边当且仅当 g c d ( a i , a j ) ≠ 1 gcd(a_i,a_j)\neq 1 gcd(ai,aj)=1。求图中割点个数.

题目思路:

显然有一个 n 2 n^2 n2建图跑割点的算法。考虑优化建图:

两个点之间有边当且仅当它们有公共的质因子。 所以我们将这些质因子拉出来当成新的点。然后对每个点权,质因分解一下,将它与它所含有的质因子建边连起来。

这样我们就得到了一个最多含有 2 n 2n 2n个点, n l o g n nlogn nlogn条边的图.(n个数最多能够含有 n n n个不同的质因子(最多的情况就是这 n n n个数是前 n n n个质数).一个数最多含有不到 l o g n logn logn个不同的质因子,最多的情况是 n n n为前 x x x个质数的累积).所以这样大大优化了建图的复杂度.

一个问题:如何证明这张图的割点不会受影响?

证明:

1.考虑割点 x x x所分割开的几个连通块,它们一定是无法不通过 x x x进行连通的。

假设分割开的任意两个连通块里各任取一个点 i , j i,j i,j.这两个点之间显然没有边直接相连.所以我们可以只证明新增的虚点不会将两个原本没有边直接相连的点连起来。

2.根据我们建图的规则,新增的虚点它所连的点在原图中的子图是一个完全图.因为它们含有公共质因子.
所以得证。

具体做法:

1.先筛出 1 e 7 \sqrt{1e7} 1e7 以内的质数,以获得每个数的质因子.用vector存下来.复杂度: O ( n a i l n a i ) O(\frac{n\sqrt{a_i}}{ln{\sqrt{a_i}}}) O(lnai nai )

2.统计有多少个质因子需要新增,且统计每个质因子点的入度.

3.建边:先新增完所有需要的质因子,然后连边.

4.跑targin求割点.特别注意:如果有一个质因子点 y y y的入度为1.即整个序列只有一个数 x x x里面出现了这个质因子 y y y,那么就不用连它们俩的边。

因为这种情况下我们跑targin的时候会将 x x x点视作割点.因为它将 y y y孤立出来了,但情况并不一定是这样。因为我们千万要记住 y y y是我们虚建的点,它本不存在。如果只是把 y y y一个点孤立出来了,就不能算是割点.

AC代码:(现在是时间 r a n k 1 rank1 rank1

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define vi vector<int>
#define vll vector<ll>
const int maxn = 3e5 + 5;
const int maxm = 3300;
const int mod = 1e9 + 7;
bool bk[maxm];
int p[maxm] , cnt;
int id[maxm * maxm];
void getp ()
{
    
    
    for (int i = 2 ; i < maxm ; i++){
    
    
        if (!bk[i]) p[++cnt] = i;
        for (int j = 1 ; j <= cnt && p[j] * i < maxm ; j++){
    
    
            bk[p[j] * i] = 1;
            if (i % p[j] == 0) break;
        }
    }
    return ;
}
int a[maxn];
vector<int> e[maxn] , f[maxn];
// targin求强连通
int dfn[maxn] , low[maxn] , tot , iscut[maxn];
void dfs (int u , int fa)
{
    
    
    dfn[u] = low[u] = ++tot;
    int s = 0;
    for (auto v : e[u]){
    
    
        if (v == fa) continue;
        if (!dfn[v]){
    
    
            s++;
            dfs(v , u);
            low[u] = min(low[u] , low[v]);
            if (low[v] >= dfn[u]) iscut[u] = true;
        }else {
    
    
            low[u] = min(low[u] , dfn[v]);
        }
    }
    // 特判根节点的情况
    if (fa == 0 && s == 1) iscut[u] = false;
}
int main()
{
    
    
    ios::sync_with_stdio(false);
    int n; cin >> n;
    getp();
    int ans = 0;
    map<int,int> mp;
    for (int i = 1 ; i <= n ; i++){
    
    
        cin >> a[i];
        int g = a[i];
        for (int j = 1 ; p[j] * p[j] <= g ; j++){
    
    
            if (g % p[j] == 0){
    
    
                f[i].push_back(p[j]);
                mp[p[j]]++;
                while (g % p[j] == 0) g /= p[j];
            }
        }
        if (g != 1) f[i].push_back(g) , mp[g]++;
    }
    // 建图
    int m = n;
    for (int i = 1 ; i <= n ; i++){
    
    
        for (auto v : f[i]){
    
    
            if (mp[v] == 1) continue;
            if (!id[v]) id[v] = ++m;
            e[i].push_back(id[v]);
            e[id[v]].push_back(i);
        }
    }
    for (int i = 1 ; i <= n ; i++) if (!dfn[i]) dfs(i , 0);
    for (int i = 1 ; i <= n ; i++) ans += iscut[i];
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35577488/article/details/114266085