Codeforces 772C 构造 数学 + dp + exgcd

首先我们能注意到两个数x, y (0 < x , y < m) 乘以倍数互相可达当且仅当gcd(x, m) == gcd(y, m)

然后我们可以发现我们让gcd(x, m)从1开始出发走向它的倍数一个一个往里加元素就好啦, 往那边走

这个可以用dp求出来, dp[ i ] 表示 gcd(x, m)从 i 开始最大元素一共有多少个, dp[ i ] = max( dp[ j ] ) + cnt[ i ]   且 i | j

然后用扩展欧几里德求出走到下一步需要乘多少。

#include<bits/stdc++.h>
#define LL long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ull unsigned long long

using namespace std;

const int N = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);

LL n, m, dp[N], path[N];
bool ban[N];
vector<LL> gg[N];
vector<LL> ans;
vector<LL> vc;

LL x, y;

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if(!b) {
        x = 1; y = 0;
        return a;
    } else {
        LL gcd, t; gcd = exgcd(b, a % b, x, y);
        t = x; x = y; y = t - (a / b) * y;
        return gcd;
    }
}

LL dfs(LL x) {
    if(~dp[x]) return dp[x];
    dp[x] = 0;
    for(int i = 2 * x; i < m; i += x) {
        LL cnt = dfs(i);
        if(cnt > dp[x]) {
            dp[x] = cnt;
            path[x] = i;
        }
    }
    dp[x] += SZ(gg[x]);
    return dp[x];
}

LL solve(LL pre, LL to) {
    if(pre == -1) ans.push_back(to);
    else {
        LL gcd = exgcd(pre, -m, x, y);
        if(to % gcd) exit(0);
        x *= to / gcd; y *= to / gcd;
        LL mo = abs(m / gcd);
        x = ((x % mo) + mo) % mo;
        ans.push_back(x);
    }
    return to;
}

int main() {
    memset(dp, -1, sizeof(dp));
    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; i++) {
        int x; scanf("%d", &x);
        ban[x] = true;
    }
    for(LL i = 1; i < m; i++) {
        if(ban[i]) continue;
        gg[__gcd(i, m)].push_back(i);
    }
    dfs(1);
    LL pre = -1;
    for(LL gcd = 1; gcd; gcd = path[gcd]) {
        for(int i = 0; i < SZ(gg[gcd]); i++)
            pre = solve(pre, gg[gcd][i]);
    }
    if(!ban[0]) ans.push_back(0);
    printf("%d\n", SZ(ans));
    for(auto& t : ans) printf("%lld ", t);
    puts("");
    return 0;
}

/*
*/

 

转载于:https://www.cnblogs.com/CJLHY/p/10424321.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值