第十六周 DYM 每日一题

这篇文章包含了一系列编程挑战,涉及RSA加密的质数检测、数组操作、寻找特定数对、数位计算、新国王游戏的求和优化、完美数的判断、Lusir的游戏策略、BFS问题、特定01序列生成及整除光棍问题的模拟除法。每个挑战都提供了思路和代码实现。
摘要由CSDN通过智能技术生成

目录

T1 RSA

思路:

代码:

T2:数组操作

思路:

代码:

T3:A-B数对

​编辑思路:

代码:

T4:数位计算

 思路:

代码:

T5:新国王游戏

思路:

代码:

T6:完美数

思路:

代码:

T7:Lusir的游戏

思路:

代码:

T8:BFS练习1

 思路:

代码:

T9:01序列2

 思路:

代码:

T10:整除光棍

 思路:

代码:


T1 RSA

思路:

 用map分别存储A和B的因子和过程中出现数,最后若俩个map均为空则说明这两个数为质数,当然首先先要判断这两个数字是否相等,如果相等那么毫无疑问是no credit,如果不相等的话则为full credit,同时如果map不为空则需要遍历map,如果有个数map的值大于等于2的话,或者为某个数的平方则输出no credit,其他的则输出partial credit。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
map<int, int >mp;
void check(ULL a)
{
	for (ULL i = 2; i <= sqrt(a); i++)
	{
		if (a % i == 0)
		{
			mp[i]++;
			mp[a / i]++;
		}
	}
}
int main()
{
	ULL a, b;
	cin >> a >> b;
	check(a);
	check(b);
	bool flag = false;
	if (a == b)
	{
		cout << "no credit" << endl;
		return 0;
	}
	for (auto i : mp)
	{
		int x = sqrt(i.first);
		if (i.second >= 2 || x * x == i.first)
		{
			flag = true;
			break;
		}
	}
	if (mp.size() == 0)
	{
		cout << "full credit" << endl;
	}
	else if(flag)
	{
		cout << "no credit" << endl;
	}
	else
	{
		cout << "partial credit" << endl;
	}
	return 0;
}

T2:数组操作

思路:

 很容易知道只能将后面的往前赋值,所有如果要相等的话,则必须是全部和最后一个相等,则我们不妨从后面开始遍历,用now来表示目前从后遍历一共有的最后的那个数,用ans来表示一共覆盖了多少次,如果遇到等于最后一个数的数则now++,如果遇到不等于最后一个的数,则now*=2来表示往前覆盖now个,并且ans++,最后当now>所包含的数即可

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int t;
	cin >> t;
	while (t--)
	{
		int n; cin >> n;
		vector<int >V(n);
		for (int i = n-1; i>=0; i--)
		{
			cin >> V[i];
		}
		int num = V[0], ans = 0, now = 1;
		while (now<n)
		{
			if (V[now] == num) 
			{
				now++;
			}
			else
			{
				now *= 2;
				ans++;
			}
		}
		cout << ans <<'\n';
	}
	return 0;
}

T3:A-B数对

思路:

一道很基础的双指针问题,首先对所输入的数进行排序,用l来指向每一个需要判断的数字,而r1来寻找a[r1]-a[l]<c最靠右边的那个数的右边第一个数,r2则为恰好满足a[r2]-r[l]>c最靠左边的那个数,则r2-r1则为l满足该该数对的总数,同时因为排序后,满足单调性,则两指针不需要刷新,且当其中一个指针大于n,或者所有数都判断完后循环结束

代码:

#include<bits/stdc++.h>
using namespace std;
int n, c;
int a[200005];
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n >> c;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    sort(a + 1, a + 1 + n);
    long long ans = 0;
    int r1 = 1;
    int r2 = 1;
    for (int l = 1; l <= n; ++l) {
        while (r1 <= n && a[r1] - a[l] < c)
            ++r1;
        while (r2 <= n && a[r2] - a[l] <= c)
            ++r2;
        if (a[r1] - a[l] == c && a[r2 - 1] - a[l] == c)
            ans += r2 - r1;
    }
    cout << ans << endl;
    return 0;
}

T4:数位计算

 思路:

采用递推,例如对于100以上1000以下的部分可以对其分割为1到9,10到99,100到999分别进行运算后求和,利用等差数列求和公式。

代码:

#include<bits/stdc++.h>
using namespace std;
const long long MOD = 998244353;
unsigned long long f[19];//f[i]表示大于10^i次方
int main()
{
	unsigned long long n, power = 10, ans = 0;
	cin >> n;
	while (true)
	{
		if (n == 10 && power == 10)
		{
			ans = 46;
			break;
		}
		else if (power == 10 && n <= power)
		{
			ans += (n + 1) * n / 2;
			break;
		}
		else if (n > power && power == 10)
		{
			ans += 45;
		}
		else if (n > power && power != 10)
		{
			unsigned long long x = (power - power / 10) % MOD;
			unsigned long long y = (power + 1 - power / 10) % MOD;
			ans += (x * y) / 2 % MOD;
		}
		else if (n < power && power != 10)
		{
			unsigned long long x = (n + 2 - power / 10) % MOD;
			unsigned long long y = (n + 1 - power / 10) % MOD;
			ans += (x * y) / 2 % MOD;
			break;
		}
		power *= 10;
	}
	cout << ans % MOD << endl;
	return 0;
}

T5:新国王游戏

思路:

首先因为n<=1e6如果挨个遍历那么时间复杂度为n^2肯定会超时,如果我们仔细观察可以发现该求和为(((((b1+0)*a2)+b2)*a3)+b3)*a4.....以此类推,则可以将求和的时间复杂度降低为n,且根据前面观察可知如果调换其中俩个人的位置u[i]和u[i+1]的话,i前面的是不会受到影响的,但是要确保该排序是最大值的排序就要满足调换前大于调换后即可,则为b[i]*a[i+1]+b[i+1]>b[i+1]*a[i]+b[i]

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
const ll maxn=1e6+5;
typedef struct
{
	ll a,b;
}u;
u ar[maxn];
bool cmp(u x,u y)
{
	return x.b*(y.a-1)>y.b*(x.a-1);
}
int main(void)
{
	ll n,i,ans;
	scanf("%lld",&n);
	for(i=0;i<n;i++)
	scanf("%lld%lld",&ar[i].a,&ar[i].b);
	sort(ar,ar+n+1,cmp);
	ans=0;
	for(i=1;i<=n;i++)
	{
		ans=(ans*ar[i].a+ar[i].b)%mod;
	}
	printf("%lld\n",ans);
	
	return 0;
}

T6:完美数

思路:

不妨逆向思维与其挨个遍历,不如设有i个a,n-i个b,只需要满足a*i+(n-1)*b所得到的数字也为完美数,再通过组合数求解即可 

其中涉及到快速幂求组合数的方法总结,以及模板如下

const int MOD = 1e9 + 7, N = 1e6 + 10;
ll fact[N], infact[N];
ll qmi(int a, int b)//快速幂
{
    ll res = 1;
    while (b)
    {
        if (b & 1) res = res * a % MOD;
        a = a * (ll)a % MOD;
        b >>= 1;
    }
    return res;
}
void init()
{
    //正求
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i++)
        fact[i] = fact[i - 1] * i % MOD;
    //反求
    infact[N - 1] = qmi(fact[N - 1], MOD - 2);
    for (int i = N - 2; i; i--)
        infact[i] = infact[i + 1] * (i + 1) % MOD;
}

int C(int a, int b)
{
    return (fact[a] * infact[b] % MOD * infact[a - b] % MOD) % MOD;
}

代码:

#include<bits/stdc++.h>
using namespace std;
#define endl '\n';
typedef long long ll;
const int MOD = 1e9 + 7, N = 1e6 + 10;
ll fact[N], infact[N];
ll qmi(int a, int b)//快速幂
{
    ll res = 1;
    while (b)
    {
        if (b & 1) res = res * a % MOD;
        a = a * (ll)a % MOD;
        b >>= 1;
    }
    return res;
}
void init()
{
    //正求
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i++)
        fact[i] = fact[i - 1] * i % MOD;
    //反求
    infact[N - 1] = qmi(fact[N - 1], MOD - 2);
    for (int i = N - 2; i; i--)
        infact[i] = infact[i + 1] * (i + 1) % MOD;
}

int C(int a, int b)
{
    return (fact[a] * infact[b] % MOD * infact[a - b] % MOD) % MOD;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int a, b, m;
    cin >> a >> b >> m;
    init();
    char c = a + '0', d = b + '0';
    int x;
    ll res = 0;
    for (int i = 0; i <= m; i++)
    {
        x = m - i;
        ll num = x * a + i * b;
        bool flag = true;
        while (num)
        {
            if (num % 10 != a && num % 10 != b)
            {
                flag = false;
                break;
            }
            num /= 10;
        }
        if (!flag)continue;
        res = (res + C(m, i)) % MOD;
    }
    cout << (res % MOD) << endl;
    return 0;
}

T7:Lusir的游戏

思路:

其实就是一个简单的二分答案,首先很容易知道要通过只需要满足大于最高建筑高度即可,则二分答案的有边界很容易就找到了, 而左边界既可以是最小值(这道题没有hank掉部分数据)也可以是0(最好设为0)

代码:

#include<bits/stdc++.h>
using namespace std;
int a[100005];
int n;
int maxn = -1e9, minn = 1e9;
bool check(int x)
{
	for (int i = 1; i <= n; i++)
	{
		if (a[i] > x)
		{
			x -= (a[i] - x);
		}
		else if(a[i]<=x)
		{
			x += (x - a[i]);
		}
		if (x < 0)return false;
		if (x >= maxn)
		{
			return true;
		}
	}
	if (x >= 0)return true;
}
int main()
{
	 cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		maxn = max(a[i], maxn);
		minn = min(a[i], minn);
	}
	int L = minn, R = maxn;
	while (L<R)
	{
		int mid = (L + R ) / 2;
		if (check(mid))
		{
			R = mid;
		}
		else
		{
			L = mid+1;
		}
	}
	cout << L << endl;
	return 0;
}

T8:BFS练习1

 思路:

就是一个简单的BFS,只不过需要标记走过的路,以免出现重复

代码:

#include<bits/stdc++.h>
using namespace std;    
typedef long long ll;
const int N = 300050;
int a[N], f[N];

inline int read() {
    int x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}

void write(int x) {
    if (x > 9) write(x / 10);
    putchar(x % 10 | '0');
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n, num;
    num = read(), n = read();
    vector<int>v(n);
    for (int i = 0; i < n; i++)
    {
        v[i] = read();
        a[v[i]] = -1;
    }
    queue<int>que;
    que.push(num);
    int res = 0;
    while (!que.empty() && n)
    {
        int len = que.size();
        for (int i = 0; i < len; i++)
        {
            int ans = que.front();
            que.pop();
            if (a[ans] == -1)
            {
                a[ans] = res;
                n--;
            }
            if (ans * 3 < 100050 && f[ans * 3] == 0)
            {
                que.push(ans * 3);
                f[ans * 3] = 1;//判断是否来过减少时间
            }
            if (ans * 2 < 100050 && f[ans * 2] == 0)
            {
                que.push(ans * 2);
                f[ans * 2] = 1;
            }
            if (ans - 1 > 0 && f[ans - 1] == 0)
            {
                que.push(ans - 1);
                f[ans - 1] = 1;
            }
            if (ans + 1 < 100050 && f[ans + 1] == 0)
            {
                que.push(ans + 1);
                f[ans + 1] = 1;
            }
        }
        res++;
    }
    for (auto i : v)
    {
        write(a[i]);
        putchar(' ');
    }

    return 0;
}

T9:01序列2

 思路:

排列中一直在变的其实是1的位置,0的位置不重要,我们只要保证两个1之间有k个0就行,剩下的位置可以随便排列。那我们就从0枚举1的数量i,然后剩下的位置全塞0就行,但0的数量最少要有(i-1)* k。为了方便其实我们可以忽略掉0,即把1都挨着,只要我们默认两个1之间有k个0就行,这样剩下01串的长度就是n-(i-1) * k,我们只要算在这个长度下,1能有多少种不同的排序即可。前面已经涉及到了组合数这里就不加赘述了

代码:


#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MOD = 1e9 + 7,N= 1000005;
LL qumi(LL a, int b)
{
	LL ans = 1;
	while (b)
	{
		if (b & 1)
		{
			ans = ans * a % MOD;
		}
		a = a * a % MOD;
		b >>= 1;
	}
	return ans;
}
LL fact[N], infact[N];
void init()//初始化
{
	fact[0] = infact[0]=1;
	for (int i = 1; i < N;i++)
	{
		fact[i] = fact[i - 1] * i % MOD;
	}
	infact[N-1] = qumi(fact[N-1], MOD - 2);
	for (int i = N - 2; i; i--)
	{
		infact[i] = infact[i + 1] * (i + 1) % MOD;
	}
}
LL C(LL a, LL b)
{
	return fact[a] * infact[b] % MOD * infact[a - b]%MOD;
}
int main()
{
	init();
	int n, k;
	cin >> n >> k;
	int i = 1;
	LL res = 1;//算上一个1没有的情况
	while (i<=n-(i-1)*k)
	{
		res = (res + C(n - (i - 1) * k, i)) % MOD;
		i++;
	}
	cout << res << endl;
	return 0;
}

T10:整除光棍

 思路:

这是一道模拟题,模拟除法,首先找到大于n的最小光棍,然后再除以n余的数假设为a,则a*10+1,再次除以n直到最后余0为止

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n; cin >> n;
	int a = 1,num=1;
	while (a<n)
	{
		a = a * 10 + 1;
		num++;
	}
	while (true)
	{
		
			cout << a / n;
			 a = a % n;
			if (a==0)
			{
				break;
			}
			a = a * 10 + 1;
			num++;
	}
	cout <<" " << num << endl;
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值