CF498C Array and Operations(数论 + 最大流)

整理的算法模板合集: 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;
}

已标记关键词 清除标记
课程简介: 历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等程,如下图所示:
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页