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

洛谷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 !
评论
  目录