codeforces1043F Make It One

本文探讨了一个复杂的算法问题,旨在找到一个整数序列中,具有最大公约数等于1的最小元素子集。通过动态规划和组合数学的方法,文章详细解释了如何在给定的数据范围内高效地解决这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Janusz is a businessman. He owns a company “Januszex”, which produces games for teenagers. Last hit of Januszex was a cool one-person game “Make it one”. The player is given a sequence of n integers ai.
It is allowed to select any subset of them, and the score is equal to the greatest common divisor of selected elements. The goal is to take as little elements as it is possible, getting the score 1. Now Janusz wonders, for given sequence, how much elements should the player choose?

Input
The first line contains an only integer n (1≤n≤300000) — the number of integers in the sequence.
The second line contains n integers a1,a2,…,an (1≤ai≤300000).

Output
If there is no subset of the given sequence with gcd equal to 1, output -1.
Otherwise, output exactly one integer — the size of the smallest subset with gcd equal to 1.


根据题目所给的数据范围可知,7个最小连续质数将超出所给的数据范围,所以子集的中元素的个数不会超过7个。
先考虑如下情况,对于所有由质数 n 倍数组成的长度为一定值的子集,那么这些子集可能的最大GCD可能为n ,也有可能为 n的某个倍数 。如果可以得出所有最大GCD为 n倍数 的子集个数,就可以求得最大GCD为 n 的子集个数。
定义dp[i][j]:为选择 i 个元素组成一个子集,其最大GCD为 j 的情况数。即所有可能的情况,减去最大GCD不是 j 的子集数。
递推关系式cnt为数组中可以被 j 整除的元素的个数,组合数的计算可以预处理阶乘和其逆元,从而可以以O(1)的复杂度计算出


#include <stdio.h>
#include <climits>
#include <algorithm>
#include <vector>
#include <utility>
#define ll long long
#define rep(index,star,finish) for(int index=star;index<finish;index++)
#define drep(index,finish,star) for(int index=finish;index>=star;index--)
#define Push(num) push_back(num)
using namespace std;
const int maxn=3e5+7;
const int mod=1e9+7;

int len;            //input size
int store[maxn];    //data of input
int con[maxn];      //the number of i's multiple
int molecule[maxn];     //molecule[i]: i!
int invMol[maxn];       //invMol[i]: 1/i!
int dp[20][maxn];       //dp[i][j] inf:  i:size of subset j:gcd of subset
ll quickpow(ll a, ll b);
int combine(int a,int b);
int main(){
    //get factorial
    molecule[0]=1;
    rep(i,1,maxn)
        molecule[i]=(1LL*i*molecule[i-1])%mod;
    //get inverse element(factorial)
    invMol[maxn-1]=quickpow(molecule[maxn-1],mod-2);
    drep(i,maxn-1,1)
        invMol[i-1]=(1LL*invMol[i]*(i))%mod;
    int peak=INT_MIN;
    scanf("%d",&len);
    rep(i,0,len){
        scanf("%d",&store[i]);
        peak=max(peak,store[i]);
        con[store[i]]++;
        if(store[i]==1){
            printf("1\n");
            return 0;
        }
    }
    //the number of i's multiple
    con[1]=len;
    rep(i,2,peak)
        for(int j=i+i;j<=peak;j+=i)
            con[i]+=con[j];
    rep(i,2,18){
        drep(j,peak,1){

           if(con[j]==0)
                continue;

           dp[i][j]=combine(i,con[j]);
           for(int k=j+j;k<=peak;k+=j){
                dp[i][j]-=dp[i][k];
                if(dp[i][j]<0)
                    dp[i][j]+=mod;
           }
        }
        if(dp[i][1]>0){
            printf("%d\n",i);
            return 0;
        }
    }
    printf("-1\n");
    return 0;
}
int combine(int a,int b){
    if(a>b)
        return 0;
    return (((1LL*molecule[b]*invMol[a])%mod)*1LL*invMol[b-a])%mod;
}
ll quickpow(ll a, ll b) {
    if (b < 0) return 0;
    int ret = 1;
    a %= mod;
    while(b) {
        if (b & 1) ret = (1LL*ret * a) % mod;
        b >>= 1;
        a = (a * a) % mod;
    }
    return ret%mod;
}


Ps:

  1. 0!=1
  2. 计算时注意数据的大小,会超出int范围。
  3. 乘法运算时, *1LL 防止溢出
  4. 由于数值较大,所以dp数组内存储的值必定经过一个较大的质数取余,在本段程序中选择的为 1e9+7。所以在减去其他情况的过程中可能致使情况数为负
    eg:设整数 a =1e9+8 ,dp数组可能存在某个位置存储的值为 a%1e9+7=1,若此时其他情况的总数为 849363,这样会导致该位置上的情况数为负
CF818G Four Melodies 题目描述 Author note: I think some of you might remember the problem “Two Melodies” from Eductational Codeforces Round 22. Now it’s time to make it a bit more difficult! Alice is a composer, and recently she had recorded two tracks that became very popular. Now she has got a lot of fans who are waiting for new tracks. This time Alice wants to form four melodies for her tracks. Alice has a sheet with � n notes written on it. She wants to take four such non-empty non-intersecting subsequences that all of them form a melody and sum of their lengths is maximal. Subsequence is a sequence that can be derived from another sequence by deleting some elements without changing the order of the remaining elements. Subsequence forms a melody when each two adjacent notes either differ by 1 or are congruent modulo 7. You should write a program which will calculate maximum sum of lengths of such four non-empty non-intersecting subsequences that all of them form a melody. 输入格式 The first line contains one integer number � n ( 4 < = � < = 3000 4<=n<=3000 ). The second line contains � n integer numbers � 1 , � 2 , . . . , � � a 1 ​ ,a 2 ​ ,...,a n ​ ( 1 < = � � < = 10 5 1<=a i ​ <=10 5 ) — notes written on a sheet. 输出格式 Print maximum sum of lengths of such four non-empty non-intersecting subsequences that all of them form a melody. 输入输出样例 #1 输入 #1 5 1 3 5 7 9 输出 #1 4 输入输出样例 #2 输入 #2 5 1 3 5 7 2 输出 #2 5 说明/提示 In the first example it is possible to compose 4 4 melodies by choosing any 4 4 notes (and each melody will consist of only one note). In the second example it is possible to compose one melody with 2 2 notes — 1 , 2 1,2 . Remaining notes are used in other three melodies (one note per each melody). 代码: C++ #include <bits/stdc++.h> using namespace std; class MinCostMaxFlow { public: MinCostMaxFlow(int n, int s, int t) : n(n), s(s), t(t) { head.assign(n + 1, -1); } void add_edge(int u, int v, int w, int c) { e.push_back(Edge{v, w, c, head[u]}); head[u] = e.size() - 1; e.push_back(Edge{u, 0, -c, head[v]}); head[v] = e.size() - 1; } bool SPFA() { now.assign(head.begin(), head.end()); dis.assign(n + 1, INF); inq.assign(n + 1, false); queue<int> q; dis[s] = 0, q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); inq[u] = false; for (int i = head[u]; ~i; i = e[i].nxt) { int v = e[i].to; if (e[i].w > 0 && dis[v] > dis[u] + e[i].c) { dis[v] = dis[u] + e[i].c; if (!inq[v]) q.push(v), inq[v] = true; } } } return dis[t] != INF; } int dfs(int u, int sum, int& cost) { if (u == t) return sum; int flow = 0; for (int i = head[u]; ~i && sum > 0; i = e[i].nxt) { int v = e[i].to; if (e[i].w > 0 && dis[v] == dis[u] + e[i].c) { int tmp = dfs(v, min(sum, e[i].w), cost); if (!tmp) dis[v] = INF; e[i].w -= tmp, e[i ^ 1].w += tmp; sum -= tmp, flow += tmp; cost += tmp * e[i].c; } } return flow; } pair<int, int> Dinic() { int maxflow = 0, mincost = 0; while (SPFA()) maxflow += dfs(s, INF, mincost); return make_pair(maxflow, mincost); } private: const int INF = 1e9; struct Edge {int to, w, c, nxt; }; int n, s, t; vector<Edge> e; vector<int> head, now, dis; vector<bool> inq; }; const int INF = 1e9; const int MAX_N = 3050; int n, a[MAX_N]; int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; int s0 = n * 2 + 1, s = n * 2 + 2, t = n * 2 + 3; MinCostMaxFlow mcmf(n * 2 + 3, s0, t); mcmf.add_edge(s0, s, 4, 0); for (int i = 1; i <= n; i++) { mcmf.add_edge(s, i, INF, 0); mcmf.add_edge(i, i + n, 1, -1); mcmf.add_edge(i + n, t, INF, 0); for (int j = i + 1; j <= n; j++) if (a[i] % 7 == a[j] % 7) { mcmf.add_edge(i + n, j, INF, 0); mcmf.add_edge(i, j, INF, 0); break; } for (int j = i + 1; j <= n; j++) if (a[i] + 1 == a[j]) { mcmf.add_edge(i + n, j, INF, 0); break; } for (int j = i + 1; j <= n; j++) if (a[i] - 1 == a[j]) { mcmf.add_edge(i + n, j, INF, 0); break; } for (int j = i + 1; j <= n; j++) if (a[i] == a[j]) { mcmf.add_edge(i, j, INF, 0); break; } } auto ans = mcmf.Dinic(); cout << -ans.second << '\n'; return 0; } 为什么在第二个样例死循环了
最新发布
08-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值