NEUQ-ACM预备队-week16

207 快快变大

区间DP

本题显然不是使用简单的贪心思想规定排序的规则就可以解决的

确定一下状态转移方程dp[1] [n]=dp[1] [k]+dp[k+1] [n]+(a1k*ak+1n)

之后使用前缀积去存储答案

#include<bits/stdc++.h>
using namespace std;
long long num[305][305];
long long dp[305][305];
#define mode 1000003
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> num[i][i];
	//计算前缀和的方式
	for (int i = 1; i <= n; i++)
	{
		for (int j = i + 1; j <= n; j++)
		{
			num[i][j] = num[j][j];
			num[i][j] = (num[i][j] * num[i][j - 1]) % mode;
		}
	}
	for (int len = 2; len <= n; len++)
	{
		for (int l = 1, r = l + len - 1; r <= n; l++,r++)//二重循环是在根据区间长度遍历所有的区间
		{
			for (int k = l; k < r; k++)//三重循环则是在遍历每一个区间里的子区间
			{
				dp[l][r] = max(dp[l][r], dp[l][k] + dp[k + 1][r] + (num[l][k] - num[k + 1][r]) * (num[l][k] - num[k + 1][r]));
			}
		}
	}
	cout << dp[1][n] << endl;
	return 0;
}

501 RSA

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/617
//本题的难点在于判断是否是partial credit
//还是采用在饿饿 饭饭2中使用过的思想 将一个数字分解成许多个质数的乘积
//如果因子出现的次数超过二 那么就不是partial credit
long long a, b;
map<int, int>Map;

int main()
{
	cin >> a >> b;
	if (a == b)
	{
		printf("no credit");
		return 0;
	}
	for (int i = 2; i <= (int)sqrt(a); i++)
	{
		if (a % i == 0)
		{
			Map[i]++;
			Map[a / i]++;
		}
	}
	for (int i = 2; i <= (int)sqrt(b); i++)
	{
		if (b % i == 0)
		{
			Map[i]++;
			Map[b / i]++;
		}
	}
	if (Map.empty())
	{
		printf("full credit");
		return 0;
	}
	for (auto i = Map.begin(); i != Map.end(); i++)
	{
		int x = sqrt(i->first);
		if (i->second >= 2||x*x==i->first)
		{
			printf("no credit");
			return 0;
		}
	}
	printf("partial credit");
	return 0;
}

502 数组操作

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/610
//从后面的数字向前面的数字赋值 即使全部的数字都与最后一个数字相同
int t;
int main()
{

	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;
}

503 A-B数对

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/616
//由于c是正整数 所以基本思路就是从大到小遍历
//由于题目说明 不同位置 相同数字的数算不同的数字 要重复计算
//那么 若使用基本的两层遍历
//题目2e5的数据 O(N^2)的时间复杂度肯定会爆
//那么考虑优化
//1、二分是必然的
//2、无法用set数组去重 因为数字重复也算多个数字
//由此 这里思考 用Map去存储 因为一个数字的位置和自己的值想必是无关的
//如果这个a-b=c合法 那么我们只需要用|a|*|b|即ab的个数积即可
//ans += i->second * j->second;
//
//此外用到了lower_bound() 能调用的绝不自己写二分
int n, c;
int ans;
map<int, int>Map;
int main()
{
	scanf("%d%d", &n, &c);
	for (int i = 0; i < n; i++)
	{
		int temp;
		scanf("%d", &temp);
		if (Map.count(temp))
			Map[temp]++;
		else
			Map[temp] = 1;
	}
	for (auto i=Map.rbegin(); i!=Map.rend() ; i++)
	{
		//lower_bound(begin, end, num)
		auto j = Map.lower_bound(i->first-c);
		for (j;j!=Map.end(); j++)
		{
			if (i->first - j->first == c)
			{
				ans += i->second * j->second;
			}	
			else
				break;
		}
	}
	printf("%d", ans);
	return 0;
}

504 数位计算

对于本题数据 直接使用循环去计算数位显然是我们不能接受的 但是一个数字 对于所有低于本位的情况都是固定的 我们可以提前处理好这些固定的数字 之后对于本位数字 我们做一个简单的处理即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int wei(ll x)
{
	int cnt = 0;
	while (x > 0)
	{
		x /= 10;
		cnt++;
	}
	return cnt;
}
ll n;
ll a, b;//首项+尾项,项数
ll ans;
ll mode = 998244353;
ll p = 10;
int main()
{
	cin >> n;
	while (1)
	{
		if (n > p - 1)
		{
			a = (p - p / 10 + 1) % mode;
			b = (p - p / 10) % mode;
			ans = (ans + (a * b / 2) % mode) % mode;
		}
		else
		{
			a = (1 + n - p / 10 + 1) % mode;
			b = (n - p / 10 + 1) % mode;
			ans = (ans + (a * b / 2) % mode) % mode;
			break;
		}
		p *= 10;
	}
	cout << ans << endl;
	return 0;
}

505 新国王游戏

这道题和之前做过的国王游戏具体没有什么差别 还是找到需要遵循贪心规则即可 而且由于出现了取模运算 并不需要使用高精度就可以完成

同时 对于每一个人 除了自己右手上的数字是浮动的 其前的所有人 左手数字的加和都没有区别 所以可以使用前缀和的思想来降低复杂度

#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;
}

506 完美数

这里主要是使用了快速幂求乘法逆元的操作 这里直接介绍一下二进制求快速幂

实际上就是用二进制数去表示任意一个数的任意次幂

就是说 如同4可以表示为0100一样 任意一个数字 由二进制表示以后 选择乘或不乘就可以组合为想要的幂次

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mode 1000000007
#define N 1000005
ll qpow(ll a, ll n)//快速幂模板
{
	ll ans = 1;
	while (n)
	{
		if (n & 1)
		{
			ans = ans*a%mode;
		}
		a = a * a % mode;
		n >>= 1;
	}
	return ans;
}
ll a, b, m;
ll f[N], nif[N];//存储阶乘与阶乘逆元
ll C(ll a, ll b)
{
	return (f[a] * nif[b] % mode * nif[a - b] % mode)%mode;//计算组合数
}
int main()
{
	cin >> a >> b >> m;
	ll ans=0;
	//预处理,我们要先计算出数字模乘的阶乘,然后算出他们的逆元
	f[0] = 1;
	nif[0] = 1;
	for (ll i = 1; i < N; i++)
	{
		f[i] = i * f[i - 1] % mode;
	}
	nif[N-1] = qpow(f[N-1], mode - 2);//n-1的逆元就是n的逆元乘以n
	for (ll i = N-2; i >= 1; i--)
	{
		nif[i] = nif[i + 1] * (i + 1) % mode;
	}
	//一切处理完毕,直接计算即可。
	for (ll i = 0; i <= m; i++)
	{
		ll cnt = i * a + (m - i) * b;
		bool flag = 0;
		while (cnt)
		{
			if (cnt % 10 != a && cnt % 10 != b)
			{
				flag = 1;
				break;
			}
			cnt /= 10;
		}
		if (flag == 1)continue;
		ans = (ans + C(m, i))%mode;
	}
	cout << ans%mode << endl;
	return 0;
}

601 BFS练习

由于给定的数字a是不变的 只是需要从a变到给定的许多个b所需要的步数 那么就不需要每次都对不同的b进行一次预处理

我们可以直接统计从给定的a到任意值需要的步骤

最后根据不同的b

直接输出答案就好

#include<bits/stdc++.h>
using namespace std;
int a, q, b;
int vis[100000+5];
queue<int>Q;
void bfs(int a)
{
	vis[a] = 0;
	Q.push(a);
	while (!Q.empty())
	{
		int temp = Q.front();
		Q.pop();
		if (temp + 1 >= 1 && temp + 1 <= 100005 && vis[temp + 1] == -1)
		{
			Q.push(temp + 1);
			vis[temp + 1] = vis[temp] + 1;
		}
		if (temp - 1 >= 1 && temp - 1 < 100005 && vis[temp - 1] == -1)
		{
			Q.push(temp - 1);
			vis[temp - 1] = vis[temp] + 1;
		}
		if (temp * 2 >= 1 && temp * 2 < 100005 && vis[temp * 2] == -1)
		{
			Q.push(temp * 2);
			vis[temp * 2] = vis[temp] + 1;
		}
		if (temp * 3 >= 1 && temp * 3 < 100005 && vis[temp * 3] == -1)
		{
			Q.push(temp * 3);
			vis[temp * 3] = vis[temp] + 1;
		}
	}
	

}
int main()
{
	memset(vis, -1, sizeof(vis));
	cin >> a >> q;
	bfs(a);
	while (q--)
	{
		cin >> b;
		cout << vis[b] << " ";
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值