整理的算法模板合集: ACM模板
首先显然的是,如果一次操作中我们选择的v不是质数,那显然把它拆成若干次v是质数的操作更优(因为任何一个合数都可以拆成若干个质数的次方的乘积,唯一分解定理,这样拆成质数最后操作的次数会更多),那么问题就变成了:每次选取满足要求的一对数,同除一个质数,问能操作多少次。我们发现题目中还有一个重要的条件: i k + j k i_k+j_k ik+jk 为奇数,那么 i k i_k ik 和 j k j_k jk 一定有一个是奇数,另一个是偶数,因此我们可以把数列中的元素按下标的奇偶分成两个集合。感觉有点像二分图了。最多的操作看上去好像是一个二分图匹配。
注意到 v v v为不同质数时的操作是不会相互影响的,因此我们将数列中的元素质因数分解,时间复杂度为 O ( n a i ) O(n\sqrt{a_i}) O(nai) ,我们发现,进行一次操作实际上就等价于,找到了一条关于质因子的匹配边,那么问题就变成了二分图最大匹配。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define x first
#define y second
#define debug(x) cout << x << "ok" << endl
using namespace std;
typedef long long ll;
//typedef __int128 int;oj上可用,编译器上不可用
const int N = 1007, M = 5007, INF = 0x3f3f3f3f;
const double eps = 1e-8;
typedef pair<int, int >PII;
int n, m, S, T;
int a[N];
int f[N][N];
int id[N][N], tot_id;
int num[N][N];
int head[N], ver[M], nex[M];
ll edge[M];
int tot, now[N];
ll maxflow;
ll deep[N];
queue<int>q;
void init()
{
memset(head, -1, sizeof head);
memset(f, 0, sizeof f);
tot_id = maxflow = 0;
tot = 0;
memset(deep, 0, sizeof deep);
}
void get_factor(int x)//分解质因数
{
f[x][0] = 0;//f[x][0]作为cnt,后面的f[x][i>0]存具体分解的质数,妙啊
int now = a[x];
//1不能选
for(int i = 2; i <= 40000; ++ i){
if(now % i == 0){
f[x][ ++ f[x][0]] = i;
id[x][f[x][0]] = ++ tot_id;
num[x][f[x][0]] = 0;
while(now % i == 0)now /= i, num[x][f[x][0]] ++ ;
}
}
if(now){
f[x][ ++ f[x][0]] = now;
id[x][f[x][0]] = ++ tot_id;
num[x][f[x][0]] = 1;
}
}
inline void add(int x,int y,int z){//建正边和反向边
ver[tot] = y;edge[tot] = z;nex[tot] = head[x];head[x] = tot ++ ;
ver[tot] = x;edge[tot] = 0;nex[tot] = head[y];head[y] = tot ++ ;
}
inline bool bfs(){//在残量网络中构造分层图
for(int i = 1; i <= tot_id + 3; ++ i)
deep[i] = INF;
while(!q.empty())q.pop();
q.push(S);deep[S] = 0;now[S] = head[S];//一些初始化
while(!q.empty()){
int x = q.front();q.pop();
for(int i = head[x];~i;i = nex[i]){
int y = ver[i];
if(edge[i] > 0 && deep[y] == INF){//没走过且剩余容量大于0
q.push(y);
now[y] = head[y];//先初始化,暂时都一样
deep[y] = deep[x] + 1;
if(y == T)return 1;//找到了
}
}
}
return 0;
}
//flow是整条增广路对最大流的贡献,rest是当前最小剩余容量,用rest去更新flow
ll dfs(int x,ll flow){//在当前分层图上增广
if(x == T)return flow;
ll ans = 0,k,i;
for(i = now[x];~i && flow;i = nex[i]){
now[x] = i;//当前弧优化(避免重复遍历从x出发的不可拓展的边)
int y = ver[i];
if(edge[i] > 0 && (deep[y] == deep[x] + 1)){//必须是下一层并且剩余容量大于0
k = dfs(y,min(flow,edge[i]));//取最小
if(!k)deep[y] = INF;//剪枝,去掉增广完毕的点
edge[i] -= k;//回溯时更新
edge[i ^ 1] += k;//成对变换
ans += k;
flow -= k;
}
//if(!flow)return rest;
}
return ans;
}
void dinic(){
while(bfs())
maxflow += dfs(S,INF);
}
void solve()
{
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
get_factor(i);
}
//debug(1);
S = tot_id + 1, T = tot_id + 2;
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= f[i][0]; ++ j){
if(i % 2)add(S, id[i][j], num[i][j]);
else add(id[i][j], T, num[i][j]);
}
}
for(int i = 1; i <= m; ++ i){
int a, b;
scanf("%d%d", &a, &b);
if(b % 2)swap(a, b);//a,b 一奇一偶,我们规定奇数在上面
int num1 = 1, num2 = 1;
while(num1 <= f[a][0] && num2 <= f[b][0]){
if(f[a][num1] == f[b][num2]){
add(id[a][num1], id[b][num2], INF);
num1 ++ ;
}
else if(f[a][num1] < f[b][num2])num1 ++ ;//因为质因数分解的时候就是升序分解
else num2 ++ ;
}
}
dinic();
printf("%lld\n", maxflow);
return ;
}
int main()
{
cin >> n >> m;
init();
solve();
return 0;
}