【解题报告】CF 2000+ 活跃思想训练2 | 前五题

本文探讨了五个不同类型的算法问题,涉及整数操作、组合优化、模稳定、最小最大值计算和整数对计数。通过数学方法和逻辑推理,展示了如何解决这些信息技术领域的挑战性问题,包括整数的最小操作次数、构造特定序列、求解整数方程、优化字符串排列和计算特定对数。
摘要由CSDN通过智能技术生成

A :Three Integers | CF1311D

题意

  • A :Three Integers | CF1311D
    给定三个整数 a ≤ b ≤ c a\le b\le c abc
    每一次操作,可以让一个数字 + 1 +1 +1 或者 − 1 -1 1
    问你,最少操作多少次,能满足:
    a ∣ b 且 b ∣ c a|b\quad 且 \quad b|c abbc
  • 样例组数 T ≤ 100 T\le 100 T100
    1 ≤ a ≤ b ≤ c ≤ 1 0 4 1\le a\le b\le c\le 10^4 1abc104

思路

  • 因为最终是满足这个整除关系,我们直接枚举 a a a ,然后枚举 k 1 , k 2 k_1,k_2 k1,k2,形成了形如 ( a , a k 1 , a k 1 k 2 ) (a,ak_1,ak_1k_2) (a,ak1,ak1k2) 三个数字
    我们的目标就是找到最小的操作次数,就是三个数的差的绝对值的和最小即可
    这样枚举,数量不会很大,大约是 O ( n log ⁡ 2 n ) O(n\log ^2 n) O(nlog2n)

代码

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 1e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

int main()
{
    int T;cin >> T;
    while(T--){
        int a,b,c;cin >> a >> b >> c;
        int ans = INF;
        int x,y,z;
        for(int i = 1;i <= 20000;++i)
        for(int j = 1;i * j <= 30000;++j)
        for(int k = 1;i * j * k <= 30000;++k){
            int tmp = abs(a-i)+abs(b-i*j)+abs(c-i*j*k);
            if(ans > tmp){
                ans = tmp;
                x = i;y = i * j;z = i * j * k;
            }
        }
        cout << ans << endl;
        cout << x << " " << y << " " << z << endl;
    }
    return 0;
}
/**

*/

B:The Football Season | CF1244C

题意

  • B:The Football Season | CF1244C
    给定 n , p , d , w n,p,d,w n,p,d,w,你需要构造出三个数字 x , y , z x,y,z x,y,z,满足:
    x w + y d = p x + y + z = n x , y , z ≥ 0 xw+yd=p\\ x+y+z=n\\ x,y,z\ge 0 xw+yd=px+y+z=nx,y,z0
    这里,每一个数字都是整数
  • 1 ≤ n ≤ 1 0 12 1\le n\le 10^{12} 1n1012
    0 ≤ p ≤ 1 0 17 0\le p\le 10^{17} 0p1017
    1 ≤ d < w ≤ 1 0 5 1\le d<w\le 10^5 1d<w105

思路

  • 光看第一个等式,其实就是拓欧求 a x + b y = c ax+by=c ax+by=c 求解,我的博客里也有
    算出通解之后,还要判断 x ≥ 0 x\ge 0 x0,且 y ≥ 0 y\ge 0 y0 x + y ≤ n x+y\le n x+yn ,只要通解带入不等式,即可得出此时 k k k (拓欧中通解的倍数 k k k) 的取值范围
    然后所有的范围求交集,然后即可构造成功。
  • 注意,交集可以使用 [ L , R ] [L,R] [L,R] 对记录,然后线段取交集的方法去做即可
    这里,由于数字特别大,不等式带入时候,由于使用了 f l o o r floor floor c e i l ceil ceil 函数,可能误差较大,还需要使用 w h i l e ( ! 合 法 ) k + + while(!合法)k++ while(!)k++ 之类的微调 k k k 的值
    还有,我使用了 i n t 128 int128 int128 进行计算

代码

  • 时间复杂度: O ( log ⁡ n ) O(\log n) O(logn)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef __int128 ll;
typedef __int128 lll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 1e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

ll ex_gcd(ll a,ll b,ll& x,ll& y) {
	if(b==0) {
		x=1;y=0;
		return a;
	}
	ll g=ex_gcd(b,a % b,x,y);
	ll tmp = x;x = y; y = tmp - a / b * y;
	return g;
}
struct node{
    ll l,r;
}tmp[5];

void uni(int x,int y){
    ll l = max(tmp[x].l,tmp[y].l);
    ll r = min(tmp[x].r,tmp[y].r);
    tmp[x].l = l;
    tmp[x].r = r;
}
int main()
{
    ll n,p,w,d;
    n = read_lll();p = read_lll();w = read_lll();d = read_lll();
    ll x0,y0,g;
    ll x,y,z;
    g = ex_gcd(w,d,x0,y0);
    if(p % g)puts("-1");
    else{
        ll k;

        k = ceil((1.0 * p / g * -x0) / (d / g));
        x = x0 * p / g + k * d / g;
        if(x < 0)k++;
        tmp[1].l = k,tmp[1].r = LINF;

        k = floor((1.0 * p / g * y0) / (w / g));
        y = y0 * p / g - k * w / g;
        if(y < 0)k--;
        tmp[2].l = -LINF,tmp[2].r = k;

        k = ceil((1.0 * n - p / g * x0 - p / g * y0) / (d / g - w / g));
        x = x0 * p / g + k * d / g;
        y = y0 * p / g - k * w / g;
        z = n - x - y;
        while(z < 0){
            k++;
            x = x0 * p / g + k * d / g;
            y = y0 * p / g - k * w / g;
            z = n - x - y;
        }

        tmp[3].l = k,tmp[3].r = LINF;

        tmp[0].l = -LINF;
        tmp[0].r = LINF;
        for(int i = 1;i <= 3;++i){
            uni(0,i);
        }
        if(tmp[0].l > tmp[0].r)puts("-1");
        else{
            k = tmp[0].l;
            x = x0 * p / g + k * d / g;
            y = y0 * p / g - k * w / g;
            z = n - x - y;
            Print(x,' ');
            Print(y,' ');
            Print(z);
        }
    }
    Write();
    return 0;
}

C:Modular Stability | CF1359E

题意

  • C:Modular Stability | CF1359E
    给定 n , k n,k n,k,你需要构造出长度为 k k k 的序列 a [ k ] a[k] a[k],满足:
    1 ≤ a 1 < a 2 ⋯ < a k ≤ n 1\le a_1<a_2\cdots <a_k\le n 1a1<a2<akn
    对于任意的正整数 x x x,对于任意的 a [ k ] a[k] a[k] 的全排列 b [ k ] b[k] b[k],都满足:
    ( x % a 1 % a 2 ⋯ % a k ) = ( x % b 1 % b 2 ⋯ % b k ) (x\%a_1\%a_2\cdots\%a_k)=(x\%b_1\%b_2\cdots\%b_k) (x%a1%a2%ak)=(x%b1%b2%bk)
  • 1 ≤ n , k ≤ 5 ⋅ 1 0 5 1\le n,k\le 5\cdot 10^5 1n,k5105

思路

  • 不管 a [ k ] a[k] a[k] 如何,等式左边的值就是 x % a 1 x\% a_1 x%a1
    也就是说让等式右边成立,就必须满足所有的 a [ k ] a[k] a[k] 都是 a 1 a_1 a1倍数
    我们枚举 a 1 a_1 a1,然后从所有 a 1 a_1 a1 的倍数中,选择 k − 1 k-1 k1 个数字放进去,就是一个简单的组合数了

代码

  • 时间复杂度: O ( n + k ) O(n+k) O(n+k)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 5e5+50;
const int MOD = 998244353;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}

ll fac[MAX],ivfac[MAX];
void init(int n){
    fac[0] = 1;
    for(int i = 1;i <= n;++i)fac[i] = fac[i-1] * i % MOD;
    ivfac[n] = inv(fac[n]);
    for(int i = n - 1;~i;--i)ivfac[i] = ivfac[i+1] * (i+1) % MOD;
}
ll C(int n,int m){
    if(n < 0 || m > n)return 0;
    return fac[n] * ivfac[m] % MOD * ivfac[n - m] % MOD;
}
int main()
{
    init(500000);
    int n,k;cin >> n >> k;
    if(k == 1){
        cout << n;
        return 0;
    }
    ll ans = 0;
    for(int i = 1;i <= n;++i){
        ans = (ans + C(n/i-1,k-1)) % MOD;
    }
    cout << ans;
    return 0;
}

D:Minimax | CF1530E

题意

  • Minimax | CF1530E
    给定一个字符串 s s s ,我们规定字符串的权值就是它所有位置的 b o r d e r border border 的最大值
    现在你需要把这个字符串重排列成 t t t ,我们需要使得它权值最小的情况下,字典序最小
    求这个 t t t
  • ∣ s ∣ ≤ 1 0 5 |s|\le 10^5 s105

思路

  • 非常复杂的一道分类讨论的题目…
  • 情况一:如果字符串中有一个字母只出现了一次,那么我们把这个字母(优先字典序最小)放在最前面,后面的按照字典序依次放,这样 b o r d e r border border 最小为 0 0 0
    例子:zaaabbbcccddd
  • 情况二:如果字符串中只有一种字母,我们也没法排列,直接输出了
    例子:zzzzzz
  • 情况三:如果字符串有两种字母,令字典序最小字母为 p 1 p_1 p1 ,字典序次小字母为 p 2 p_2 p2 ,并记他们的出现次数 c n t cnt cnt
    如果 c n t [ p 1 ] ≤ c n t [ p 2 ] + 2 cnt[p1]\le cnt[p2]+2 cnt[p1]cnt[p2]+2,我们可以这么放:aabababab ,此时 b o r d e r = 1 border=1 border=1
    否则,我们只能这么放:abbbbbbbaaaaaaaaa ,此时 b o r d e r = 1 border=1 border=1
  • 情况四:如果字符串有多于两种字母
    如果 c n t [ p 1 ] ≤ o t h e r s + 2 cnt[p1]\le others+2 cnt[p1]others+2,我们仍然可以这么放:aabacacadaefg ,此时 b o r d e r = 1 border=1 border=1
    否则,我们取字典序第三小的字母为 p 3 p_3 p3 ,我们先放一个 p 1 p_1 p1,然后放一个 p 2 p_2 p2,然后把所有的 p 1 p_1 p1 放完,然后放一个 p 3 p_3 p3,然后其他字母按照字典序放,就是:abaaaaaacbbbcccdddeee ,此时 b o r d e r = 1 border=1 border=1

代码

  • 时间复杂度: O ( ∣ s ∣ ) O(|s|) O(s)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 1e5+50;
const int MOD = 998244353;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

char ss[MAX];
int cnt[30];

int main()
{
    int T;scanf("%d",&T);
    while(T--){
        scanf("%s",ss);
        int p1,p2,p3,p4 = 0;
        int zhonglei = 0;
        for(int i = 0;i < 26;++i)cnt[i] = 0;
        for(int i = 0,ed = strlen(ss);i < ed;++i){
            cnt[ss[i] - 'a']++;
            if(cnt[ss[i] - 'a'] == 1)zhonglei++;
        }
        for(int i = 0;i < 26;++i)if(cnt[i]){
            p1 = i;break;
        }
        for(int i = p1+1;i < 26;++i)if(cnt[i]){
            p2 = i;break;
        }
        for(int i = p2+1;i < 26;++i)if(cnt[i]){
            p3 = i;break;
        }
        for(int i = 0;i < 26;++i)if(cnt[i] == 1){
            p4 = i;break;
        }

        if(cnt[p4] == 1){
            putchar('a' + p4);cnt[p4]--;
            for(int i = 0;i < 26;++i)for(int j = 1;j <= cnt[i];++j)putchar('a' + i);
        }else if(zhonglei == 1){
            for(int i = 1;i <= cnt[p1];++i)printf("%c",'a' + p1);
        }else if(zhonglei == 2){
            if(cnt[p1] > cnt[p2] + 2){
                putchar('a' + p1);cnt[p1]--;
                for(int i = 1;i <= cnt[p2];++i)putchar('a' + p2);
                for(int i = 1;i <= cnt[p1];++i)putchar('a' + p1);
            }else{
                putchar('a' + p1);cnt[p1]--;
                while(1){
                    if(!cnt[p1] && !cnt[p2])break;
                    if(cnt[p1])putchar('a' + p1),cnt[p1]--;
                    if(cnt[p2])putchar('a' + p2),cnt[p2]--;
                }
            }
        }else{
            if(cnt[p1] - 2 > strlen(ss)- cnt[p1]){
                putchar('a' + p1);cnt[p1]--;
                putchar('a' + p2);cnt[p2]--;
                for(int i = 1;i <= cnt[p1];++i)putchar('a' + p1);
                putchar('a' + p3);cnt[p3]--;
                for(int i = p2;i < 26;++i)for(int j = 1;j <= cnt[i];++j)putchar('a' + i);
            }else{
                putchar('a' + p1);cnt[p1]--;
                for(int i = 1;i <= cnt[p1];++i){
                    putchar('a' + p1);
                    if(cnt[p2]){
                        putchar('a' + p2);
                        cnt[p2]--;
                        while(p2 < 26 && cnt[p2] == 0)p2++;
                    }
                }
                for(int i = p2;i < 26;++i)for(int j = 1;j <= cnt[i];++j)putchar('a' + i);
            }

        }

        puts("");
    }
    return 0;
}

E:The Number of Pairs | CF1499D

题意

  • E:The Number of Pairs | CF1499D
    给定 c , d , x c,d,x c,d,x,求有多少对正整数 ( a , b ) (a,b) (a,b),满足:
    c ⋅ l c m ( a , b ) − d ⋅ gcd ⁡ ( a , b ) = x c\cdot lcm(a,b)-d\cdot \gcd(a,b)=x clcm(a,b)dgcd(a,b)=x
  • 1 ≤ c , d , x ≤ 1 0 7 1\le c,d,x\le 10^7 1c,d,x107

思路

  • 我们分解 a = ∏ p i s i a=\prod p_i^{s_i} a=pisi b = ∏ p i t i b=\prod p_i^{t_i} b=piti
    根据定义, l c m = ∏ p i max ⁡ { s i , t i } lcm=\prod p_i^{\max\{s_i,t_i\}} lcm=pimax{si,ti} gcd ⁡ = ∏ p i min ⁡ { s i , t i } \gcd=\prod p_i^{\min\{s_i,t_i\}} gcd=pimin{si,ti}
    首先容易得到 gcd ⁡ ∣ l c m \gcd|lcm gcdlcm,我们令 l c m = r ⋅ gcd ⁡ lcm=r\cdot \gcd lcm=rgcd,带入得到:
    gcd ⁡ ( a , b ) = x c r − d \gcd(a,b)=\frac{x}{cr-d} gcd(a,b)=crdx
    由于 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b) 一定是一个整数,容易得到 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b) 一定是 x x x 的一个因子
    那我们 O ( x ) O(\sqrt x) O(x ) 去枚举 x x x 的所有因子 p p p,我们就相当于是枚举所有的 gcd ⁡ ( a , b ) = p \gcd(a,b)=p gcd(a,b)=p
    我们带入式子,就可以得到 l c m ( a , b ) = x + p d c lcm(a,b)=\frac{x+pd}{c} lcm(a,b)=cx+pd,也必须要满足 c ∣ ( x + p d ) c|(x+pd) c(x+pd)
    还必须要满足 gcd ⁡ ∣ l c m \gcd | lcm gcdlcm
  • 那么问题来了,我们得到了 gcd ⁡ ( a , b ) = G \gcd(a,b)=G gcd(a,b)=G l c m ( a , b ) = L lcm(a,b)=L lcm(a,b)=L
    此时的满足要求的 ( a , b ) (a,b) (a,b) 对有多少呢?注意到我们必须 O ( 1 ) O(1) O(1) 去得到
    反过来看定义:根据定义, l c m = ∏ p i max ⁡ { s i , t i } lcm=\prod p_i^{\max\{s_i,t_i\}} lcm=pimax{si,ti} gcd ⁡ = ∏ p i min ⁡ { s i , t i } \gcd=\prod p_i^{\min\{s_i,t_i\}} gcd=pimin{si,ti}
    如果 s i = t i s_i=t_i si=ti ,那么 p i p_i pi 的次数, a , b a,b a,b 两个数都是相同的。
    但是如果 s i ≠ t i s_i\ne t_i si=ti,那么可以 s i > t i s_i>t_i si>ti ,也可以 s i < t i s_i<t_i si<ti ,也就是说对于这个 p i p_i pi,我们的 ( a , b ) (a,b) (a,b) 对于这个质因数有两种选择
  • 也就是说,我们令 l g = L / G lg=L/G lg=L/G,计算出 l g lg lg 有多少个不同的质因子的个数 s h u shu shu ,我们的答案就是 2 s h u 2^{shu} 2shu
    这个可以在我们埃筛的过程中实现

代码

  • 时间复杂度: O ( x log ⁡ x ) O(x\log x) O(xlogx)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 2e7+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;


int cnt[MAX];
bool vis[MAX];
ll p2[150];
void init(int n){
    for(int i = 2;i <= n;++i){
        if(vis[i])continue;
        for(int j = 1;i * j <= n;++j){
            vis[i * j] = 1;
            cnt[i * j]++;
        }
    }
}

int main()
{
    init(20000000);
    p2[0] = 1;
    for(int i = 1;i <= 100;++i){
        p2[i] = p2[i-1] * 2;
    }
    IOS;int T;cin >> T;
    while(T--){
        ll c,d,x;cin >> c >> d >> x;
        ll ans = 0;
        for(ll g = 1;g * g <= x;++g){
            if(x % g)continue;
            ll l = x + d * g;
            if(l % c == 0){
                l = l / c;
                ll lg = l / g;
                if(l % g == 0)ans += p2[cnt[lg]];
            }
            l = x + d * (x / g);
            if(g * g != x && l % c == 0){
                l = l / c;
                ll lg = l / (x / g);
                if(l % (x / g) == 0)ans += p2[cnt[lg]];
            }
        }
        cout << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值