嘘~ 正在从服务器偷取页面 . . .

洛谷P2065 [TJOI2011] 卡片 题解


洛谷P2065 [TJOI2011] 卡片 题解

题目链接:P2065 [TJOI2011] 卡片

题意

桌子上现在有 \(n\) 张蓝色卡片和 \(m\) 张红色卡片,每张卡片上有一个大于 \(1\) 的整数 \(x\)

现在你要从桌子上拿走一些卡片,分若干次拿。

每次只能拿走一组卡片:这组卡片颜色不同,并且两张卡片上面的数字的最大公约数大于 \(1\)

问最多可以从桌上拿走多少组卡片。

输入格式

文件的第一行读入一个整数 \(T\) ,为数据组数。

对于每组数据,第一行给出 \(n,m\)

第二行给出每张蓝色卡片上面的数字,第三行给出每张红色卡片上的数字。

输出格式

对每组测试数据,输出最多可以拿走多少组卡片。

数据范围

\(1\le T\le 100,~1\le m,n\le 500\) ,卡片上的数字 \(x\) 满足 \(1 \le x \le 10^7\)

虽说网络流复杂度很宽松,但是直接 \(\mathcal{O}(n^2)\) 暴力建边是不行滴。

考虑对卡片上的每个数质因数分解,称这些质因数的并为点集 \(P\)

\(A\) 为蓝色卡片的点集,称 \(B\) 为红色卡片的点集。

  • 每张蓝色卡片向其所含的素因子连边,流量为 \(1\)
  • 每张红色卡片所含的素因子向这张卡片连边,流量为 \(1\)
  • 源点 \(s\)\(A\) 连边,流量为 \(1\)\(B\) 向汇点 \(t\) 连边,流量为 \(1\)

跑一遍最大流就是答案了。然后分析一波复杂度。

点数 \(\mathcal{O}(2n + \overline{\omega} n) = \mathcal{O}(c_1n)\) ,边数 \(\mathcal{O}(4\overline{\omega}n + 4n) = \mathcal{O}(c_2n)\)

其中 \(\overline\omega\) 表示每个数贡献的质因子个数的一个期望值。

这个 \(\overline\omega\) 的范围需要分析一下。注意到当一个数贡献了 \(k\) 个质因子时,至少有一个质因子小于 \(\sqrt[k]{M}\)

当贡献 \(2\) 个因子时,尽管此时有很多质因子,但这样的总贡献不会大于 \(2n\)

当贡献 \(3\) 个因子时,至少有一个质因子小于 \((\sqrt[3]{M} \approx 216)\),而 \(n\le 500\) ,可以发现至多贡献 \(648\)

当贡献 \(4\) 个因子时,至多贡献 \(224\) 个质因子。归纳法可知越往后贡献越小,还不如贡献 \(2\) 个因子的时候。

因此事实上 \(\overline\omega \le 2\) ,在数据随机的时候甚至可以小于 \(1\) ,因此我们可以得到 \(c_1 \le 4,c_2 \le 12\)

理论时间复杂度上界为 \(\mathcal{O}\left(Tc_1^2c_2n^3+T\frac{2\sqrt{M}}{\ln M}\right)\) ,把 \(n=500\) 代入可以发现数量级大约 \(2\times 10^{12}\)

然而这题的数据还是很随机的,并且这个上界很难达到,所以能过。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
// N = 2015, M = 8060
#define N ((int)(2 * 500 + 2 * 500 + 15))
#define M ((int)((2 * 500 * 2 + N + 15) * 2))
#define K ((int)(3355))

unordered_map<int,int> vis;
// A: 1 ~ n, B: n+1 ~ n+m, P: n+m+1 ~ n+m+k
int n,m,k,s,t,tot,cnt,pos=1,pri[K],p[K],head[N],now[N],dep[N];
struct Edge { int u,v,w,next; } e[M];
void addEdge(int u,int v,int w)
{ e[++pos] = {u,v,w,head[u]}; head[u] = pos; }
void add(int u,int v,int w) { addEdge(u,v,w); addEdge(v,u,0); }
queue<int> q; bitset<K> ck;
void init_prime()
{
    for(int i=2; i<=K-5; i++)
    {
        if(!ck[i]) pri[++cnt] = i;
        for(int j=1; j<=cnt && i * pri[j] <= K-5; j++)
        { ck[i * pri[j]] = 1; if(i % pri[j] == 0) break;} 
    }
}
bool bfs()
{
    for(int i=1; i<=tot; i++) dep[i] = INF;
    dep[s] = 1; q.push(s); now[s] = head[s];
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i=head[u]; i; i=e[i].next)
        {
            int v = e[i].v;
            if(e[i].w > 0 && dep[v] == INF)
            {
                dep[v] = dep[u] + 1;
                now[v] = head[v]; q.push(v);
            }
        }
    }
    return dep[t] != INF;
}
int dfs(int u,int in)
{
    if(u == t) return in;
    int out = 0;
    for(int i=now[u]; i && in; i = e[i].next)
    {
        int v = e[i].v; now[u] = i;
        if(e[i].w > 0 && dep[v] == dep[u] + 1)
        {
            int res = dfs(v, min(in, e[i].w));
            e[i].w -= res; e[i ^ 1].w += res;
            in -= res; out += res;
        }
    }
    if(!out) dep[u] = INF;
    return out;
}
int Dinic()
{
    int res = 0;
    while(bfs()) res += dfs(s,INF);
    return res;
}
void proc1(int x,int id)
{
    int o = 0;
    for(int i=1; i<=cnt && pri[i] * pri[i] <= x; i++)
        if(x % pri[i] == 0)
        { p[++o] = pri[i]; while(x % pri[i] == 0) x /= pri[i]; }
    if(x > 1) p[++o] = x;
    for(int i=1; i<=o; i++)
    {
        if(!vis[p[i]]) vis[p[i]] = ++k;
        add(id, n + m + vis[p[i]], 1);
    }
}
void proc2(int x,int id)
{
    int o = 0;
    for(int i=1; i<=cnt && pri[i] * pri[i] <= x; i++)
        if(x % pri[i] == 0)
        { p[++o] = pri[i]; while(x % pri[i] == 0) x /= pri[i]; }
    if(x > 1) p[++o] = x;
    for(int i=1; i<=o; i++)
        if(vis[p[i]]) add(n + m + vis[p[i]], id, 1);
}
void clear()
{
    pos = 1; k = 0; vis.clear();
    for(int i=1; i<=tot; i++) head[i] = 0;
}
void solve()
{
    clear(); cin >> n >> m;
    for(int i=1,x; i<=n; i++) { cin >> x; proc1(x, i); }
    for(int i=1,x; i<=m; i++) { cin >> x; proc2(x, n + i); }
    s = n + m + k + 1; tot = t = s + 1;
    for(int i=1; i<=n; i++) add(s, i, 1);
    for(int i=1; i<=m; i++) add(n + i, t, 1);
    cout << Dinic() << '\n';
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    init_prime();
    int _Q; cin >> _Q; while(_Q--) solve();
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录