2023 hbcpc

2023 hbcpc 补题

I Step

题目大意
求最小的 t ,使得对于 k ∈ [ 1 , n ] , E i t i = 1.. i 为 a k 的倍数。 求最小的 t ,使得对于 k ∈ [1, n] , E_i^t i=1.. i为 a_k的倍数。 求最小的t,使得对于k[1,n]Eiti=1..iak的倍数。
题解

设t = lcm(p1,p2…pn),那么所求即可转换为:

1+2+3+…+m = m(m+1)*0.5

求最小的m,使得满足:

m(m+1) = 2t

不妨,设2t = ab; (a|m,b|(m+1)); 且ax=m,by=m+1;

那么有,a(-x)+by = 1; 可以用exgcd来求出来最小的正整数x

关键在于对于a的枚举,可以对t进行质因数分解,枚举每个质因子是属于m还是属于m+1

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#include <cstring>
#include <vector>
#include <unordered_map>
#include <algorithm>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
#define ll long long
const int N = 1e6 + 10;

int n;
int p[N];
unordered_map<LL, LL> mp;
vector<int> v;

void fuc(LL n) {
    for (LL i = 2; i <= n / i; ++ i) {
        if (n % i == 0) {
            mp[i] = 1;
            while (n && n % i == 0) {
                mp[i] *= i;
                n /= i;
            }

            v.push_back(i);
        }
    }

    if (n > 1) v.push_back(n), mp[n] = n;
}

ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(!b)
	{
		x = 1,y = 0;
        return a;
	}
	ll d = exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}

ll lcm(ll x,ll y)
{
	return 1ll*x/__gcd(x,y)*y;
}

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin >> p[i];
    ll t = lcm(p[1],1);
    for(int i=1;i<=n;i++) t = lcm(t,p[i]);
    
    fuc(1ll*t*2);
    
    ll res = 0x3f3f3f3f3f3f3f3f;
    
    //二进制枚举
    for(ll i=0;i<(1<<v.size());i++)
    {
    	ll a = 1;
    	for(ll j = 0;j<1ll*v.size();j++)
    	{
    		if(i>>j&1) a*=mp[v[j]];//判断第j位是1还是0
    	}
    	ll b = 2*t/a;
        if(__gcd(a,b)!=1) continue;
        ll x,y;
        ll d = exgcd(a,b,x,y);
        x = -x;
        x = (x%b/d+b/d)%(b/d);//求x的最小整数解
        if(1ll*a*x) res = min(res,a*x);
    }
    cout <<res;
	return 0;
}

J Expansion

题目大意
给定长度为 n 的序列 a n 。初始时没有资源,且已经占据第一个点,从 第一个点开始依次向右占领。假设已经占领到第 x 个点,则每秒资源增加 a 1 + a 2 + . . + a x 。 问保证在无穷时间内资源数量非负的情况下,最快何时能占领完这 n 个点。 给定长度为 n 的序列 {an}。初始时没有资源,且已经占据第一个点,从\\ 第一个点开始依次向右占领。假设已经占领到第x个点,则每秒资源增加\\ a_1+a_2+..+a_x。\\问保证在无穷时间内资源数量非负的情况下,最快何时能占领 完这 n 个点。 给定长度为n的序列an。初始时没有资源,且已经占据第一个点,从第一个点开始依次向右占领。假设已经占领到第x个点,则每秒资源增加a1+a2+..+ax问保证在无穷时间内资源数量非负的情况下,最快何时能占领完这n个点。
题解

先分析什么情况下解不存在

注:pre[i] 表示a[i]的前缀和 mx[i]表示1~i中pre[i]的最大值

由题意,必须保证每时每刻的资源数必须为非负数

1.那么如果pre[n]<0,则表示资源数会变为负数,无解

2.当到一个点i时资源数为负数且mx[i]<=0时,无解

解存在时,当到一个点资源数变为负数时,为了最少时间,需要在前i个点中pre[i]最大的那个点中,即mx[i],多停留u秒

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include<iomanip>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e5+10,mod = 998244353;
#define ll long long
ll n,m;
ll pre[N]={0},mx[N]={0};
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		ll x;
		cin>>x;
		pre[i] = pre[i-1]+x;
		mx[i] = max(mx[i-1],pre[i]);
	}
	if(pre[n]<0) {cout<<-1<<endl; return 0;}
    ll add = 0,ans = 0;
    for(int i=1;i<=n;i++)
    {
    	ans++;
        add = add + pre[i];
    	if(add<0)
    	{
    		if(mx[i]<=0) {cout<<-1<<endl; return 0;}
            ll u = (-1*add+mx[i]-1)/mx[i];
            //求最小的k,使得k*mx[i]>=abs(add);
            add = add  + u * mx[i];
            ans = ans + u;
    	}
    }
    cout << ans;
	return 0;
}

K Dice Game

题目大意

n + 1 个人每个人各投一个 m 面的骰子,点数最小的人失败。如果有多个 最小值,则继续投直到出现失败者。问一号的骰子一直投出 x,其余人随 机使得一号失败的概率,对 1 ⩽ x ⩽ m 输出答案。

题解

本题主要在于分析出[2,n]号人对于游戏的贡献是相同且独立的,所以只需要考虑一个人的情况即可

举例:
如果 2 号第一次丢骰子投出大于 x ,则 1 号直接输, P r [ 1 ] = ( m − x ) / m 对于 2 号第 i 次丢骰子,前 i − 1 次都等于 x ,第 i 次大于 x ,则 1 号输。 有概率 P r [ i ] = ( m − x ) / m i 。 因而 1 号输的概率为 ( m − x ) / ( m − 1 ) 用等比数列计算 + 求极限 则最后的结果为 ( ( m − x ) / ( m − 1 ) ) n 如果 2 号第一次丢骰子投出大于 x,则 1 号直接输,Pr[1] = (m−x)/m \\ 对于 2 号第 i 次丢骰子,前 i − 1 次都等于 x,第 i 次大于 x,则 1 号输。\\ 有概率 Pr[i] = (m−x)/m^i。\\ 因而 1 号输的概率为 (m-x)/(m-1) {用等比数列计算+求极限}\\ 则最后的结果为 ((m-x)/(m-1))^n 如果2号第一次丢骰子投出大于x,则1号直接输,Pr[1]=(mx)/m对于2号第i次丢骰子,前i1次都等于x,第i次大于x,则1号输。有概率Pr[i]=(mx)/mi因而1号输的概率为(mx)/(m1)用等比数列计算+求极限则最后的结果为((mx)/(m1))n

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include<iomanip>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e6+10,mod = 998244353;
#define ll long long
ll qmi(ll a,ll b)
{
	ll res = 1;
	while(b)
	{
		if(b%2) res = res*a%mod;
		a = a*a%mod;
		b>>=1;
	}
	return res;
}

ll inv(ll x) 
{
	return qmi(x,mod-2);
}
ll n,m;
int main()
{
	cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
    	ll ans = qmi((m-i)*inv(m-1)%mod,n);
    	cout<<ans<<" ";
    }
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值