牛客练习赛21


A题

链接:https://www.nowcoder.com/acm/contest/130/A
来源:牛客网

题目描述

黑妹最近在玩一个有趣的游戏,游戏规则是这样的:
刚开始黑板上有三个不同的初始数字,然后黑妹每一次可以选择黑板上的两个不同的数字,然后计算出这两个不同数字 之差的绝对值,如果黑板上没有这个数字,那么就把这个新数字写在黑板上。
黑妹已经玩腻了这个游戏,现在黑妹想知道她最多能添加多少个数字。

输入描述:

第一行一个整数T表示数据组数。(1≤T≤100000)
接下来T行每行三个整数 a,b,c 表示黑板上的三个初始数字。()

输出描述:

对于每组数据输出一行表示答案。


思路:

联想更相减损术,可以发现,如果a, b, c是三个互素的数(gcd(a, gcd(b, c)) == 1),那么一定可以得到[1, max(a, max(b, c))]上的所有数。

如果gcd(a, gcd(b, c)) != 1, 那么一定可以得到gcd(a, gcd(b, c)) * 1, gcd(a, gcd(b, c)) * 2...gcd(a, gcd(b, c)) * max(a, max(b, c)) / gcd(a, gcd(b, c));


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector<int> vi;
typedef vector<ll> vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

vll p(MAXN);

ll gcd(ll a, ll b){
	if(!b)
		return a;
	return gcd(b, a % b);
}

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	int times;
	cin >> times;
	while(times--){
		for(int i = 0; i < 3; i++)
			cin >> p[i];
		sort(p.begin(), p.begin() + 3);
		cout << p[2] / gcd(p[2], gcd(p[0], p[1])) - 3 << endl;
	}
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}

B题

链接:https://www.nowcoder.com/acm/contest/130/B
来源:牛客网

题目描述

黑妹和黑弟又聚在一起玩游戏了,这次他们选择在一个n*m的棋盘上玩游戏,棋盘上的每个方格都有一个非负的分数,
游戏从左上角开始右下角结束,双方交替的选择一个方格并获得方格上相应的分数,一方选择的方格必须在上一步另一方选择的方格
的右边或者下面,黑妹先开始。现在黑妹想知道,如果双方都采取最优策略(最优策略是指双方都希望最终自己的总分数减去对方的总分数最大),她的总分数减去黑弟的总分数会是多少?

输入描述:

第一行一个整数T表示数据的组数。(1 ≤ T ≤ 20)
对于每组数据:
第一行两个整数n,m表示棋盘的规格。(1 ≤ n, m ≤ 500)
接下来n行每行m个整数aij表示方格对应的分数。()

输出描述:

对于每组数据输出一行表示答案。


思路:

每次双方都会选对自己最有利的,也就是说,在(i, j)上选max(dp[i + 1][j], dp[i][j + 1]);

dp[i][j] = mat[i][j] - max(dp[i + 1][j], dp[i][j + 1]);


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

vii dp, mat;

int recur(int i, int j){
	if(i == n - 1 && j == m - 1)
		return dp[i][j] = mat[i][j];
	if(i >= n || j >= m)
		return -INF;
	if(dp[i][j] > -INF)
		return dp[i][j];
	return dp[i][j] = mat[i][j] - max(recur(i + 1, j), recur(i, j + 1));
}

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	int times;
	cin >> times;
	while(times--){
		cin >> n >> m;
		mat.clear();
		mat.resize(n, vi(m));
		for(int i = 0; i < n; i++)
			for(int j = 0; j < m; j++)
				cin >> mat[i][j];
		dp.clear();
		dp.resize(n + 2, vi(m + 2, -INF));
		cout << recur(0, 0) << endl;
	}
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


C题

链接:https://www.nowcoder.com/acm/contest/130/C
来源:牛客网

题目描述

黑妹又开始玩起了一个游戏,这次她面对的是一个序列,序列里的每个数字都是正整数,黑妹每次可以从这个序列里选择两个数字,然后将这两个数字除以它们任意一个公共因子。
如果 x 是 a 和 b 的公共因子,当且仅当 a 和 b 都能被 x 整除。
黑妹很快玩腻了这些操作,她现在想知道,这个序列经过一些操作之后,将新序列的每个元素都乘起来的最小乘积是多少,由于这个乘积可能很大,所以你需要告诉黑妹这个乘积对10 9+7取模之后的值。

输入描述:

第一行一个整数T表示数据的组数。(1 ≤ T ≤ 10)

对于每组数据:

第一行n表示序列的长度。(1 ≤ n ≤ 10000)

接下来一行n个整数ai表示序列的每个元素。(1 ≤ ai ≤ 108)

输出描述:

对于每组数据输出一行表示答案。


思路:

首先,a与b同时除以公共素因子一定是最优的。

所以把有某个相同素因子的数选出来,从每个数拥有的此素因子数幂从大到小选择,每次把最大的两个除以这个素因子, 这样可以使得各个数拥有的素因子幂平均,也就会最优。


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

vi p, pme;

bitset<MAXN> prime;

typedef struct Node{
	int cnt, id;
	Node(int a, int b){
		id = a;
		cnt = b;
	}
	bool operator < (const Node &rhs) const{
		return cnt < rhs.cnt;
	}
}Node;

bool isprime(int x){
	if(x < 2)
		return false;
	if(x <= 3)
		return true;
	for(int i = 2; i <= sqrt(x); i++)
		if(x % i == 0)
			return false;
	return true;
}

ll qp(ll a, ll b, ll m){
	if(!b)
		return 1 % m;
	if(b & 1)
		return a * qp((a * a) % m, b >> 1, m) % m;
	return qp((a * a) % m, b >> 1, m);
}

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	for(int i = 2; i <= 1e4; i++)
		if(!prime[i] && isprime(i)){
			pme.push_back(i);
			for(int j = i + i; j <= 1e4; j += i)
				prime[j] = 1;
		}
//	cerr << pme.size() << endl;
	int times;
	cin >> times;
	while(times--){
		cin >> n;
		p.clear();
		p.resize(n);
		for(int i = 0; i < n; i++)
			cin >> p[i];
		ll res = 1;
		for(int i = 0; i < (int)pme.size(); i++){
			priority_queue<int> q;
			for(int j = 0; j < n; j++){
				if(p[j] % pme[i] == 0){
					int cnt = 0;
					while(p[j] % pme[i] == 0){
						p[j] /= pme[i];
						cnt++;
					}
					q.push(cnt);
				}
			}
			while(q.size() >= 2){
				int u = q.top();
				q.pop();
				int v = q.top();
				q.pop();
				if(u - 1)
					q.push(u - 1);
				if(v - 1)
					q.push(v - 1);
			}
			if(!q.empty())
				res = res * qp(pme[i], q.top(), MOD) % MOD;
		}
		sort(p.begin(), p.end());
		for(int i = 1; i < n; i++)
			if(p[i - 1] == p[i])
				p[i - 1] = p[i] = 1;
		for(int i = 0; i < n; i++)
			res = res * (ll)p[i] % MOD;
		cout << res << endl;
	}
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


D题

链接: https://www.nowcoder.com/acm/contest/130/D
来源:牛客网

题目描述

黑妹又来玩游戏了,这次她面对的是两个长度为n的正整数序列A和B,并且她要维护两个二元序对集合S1和S2,初始时这两个集合是空的,每一次她可以进行一次操作,
选择满足以下条件的两个序对(i, j)和(p, q),
1.(i, j)不在S1中并且(p, q)不在S2中。
2. A i < B j, A q > B p
3. A i 和 B j 不互质, A q 和 B p 不互质。
4. gcd(A i, B j) 和 gcd(A q, B p) 不互质。
然后把(i, j)加入集合S1,(p, q)加入集合S2。
但是她又很快玩腻了这个游戏,她现在想知道她最多能进行多少次操作。

输入描述:

第一行一个整数T表示测试数据组数。(1 ≤ T ≤ 10)

对于每组数据:

第一行一个整数n表示序列的长度。(1 ≤ n ≤ 400)

接下来一行n个整数表示序列A的每个元素。(1 ≤ Ai ≤ 109)

接下来一行n个整数表示序列B的每个元素。(1 ≤ Bi ≤ 109)

输出描述:

对于每组数据输出一行表示答案。

思路:

发现题意是有数据关系的,优先考虑建图。

把每个点对当作一个点,根据题意建边,用dinic算法跑最大流。

为了在限制时间与空间内计算完成,可以进行优化以及压缩。

优化的方法是把素因子当成中间点,放在原来两点的连线上。

压缩的方法是把最大公约数相同的点当成一个点,记录次数,在建边时根据次数定容量。


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

int gcd(int a, int b){
	if(!b)
		return a;
	return gcd(b, a % b);
}

set<int> prime;
bitset<MAXN> isnotprime;

map<int, int> l, r;
map<int, int> prime_loc;

void generate_prime(){
	for(int i = 2; i <= sqrt(1e9); i++){
		if(isnotprime[i])
			continue;
		for(int j = i + i; j <= sqrt(1e9); j += i)
			isnotprime[j] = 1;
	}
	for(int i = 2, j = 0; i <= sqrt(1e9); i++){
		if(!isnotprime[i]){
			prime.insert(i);
			prime_loc[i] = j++;
		}
	}
}

typedef struct star{
	int u, v, nt, cap;
	star(int a, int b, int c, int d){
		u = a;
		v = b;
		nt = c;
		cap = d;
	}
}star;

vi head;
vector<star> st;

void add(int u, int v, int c){
	st.push_back(star(u, v, head[u], c));
	head[u] = st.size() - 1;
}

vi level;

int recur(int u, int e, int remain){
	if(u == e || !remain)
		return remain;
	int res = 0;
	for(int i = head[u]; ~i; i = st[i].nt){
		int v = st[i].v;
		int c = st[i].cap;
		if(level[v] != level[u] + 1 || !c)
			continue;
		int cur;
		if((cur = recur(v, e, min(remain, c)))){
			res += cur;
			st[i].cap -= cur;
			st[i ^ 1].cap += cur;
			remain -= cur;
			if(!remain)
				return res;
		}
	}
	if(!res)
		level[u] = 0;
	return res;
}

vi A, B;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;

	generate_prime();
	int times;
	cin >> times;
	while(times--){
		cin >> n;
		A.clear();
		A.resize(n);
		B.clear();
		B.resize(n);
		for(int i = 0; i < n; i++)
			cin >> A[i];
		for(int i = 0; i < n; i++)
			cin >> B[i];
		l.clear();
		r.clear();
		for(int i = 0; i < n; i++)
			for(int j = 0; j < n; j++){
				int g = gcd(A[i], B[j]);
				if(g == 1 || A[i] == B[j])
					continue;
				if(A[i] < B[j])
					l[g]++;
				else
					r[g]++;
			}
		int s = 0;
		int e = 1;
		head.clear();
		head.resize(l.size() + r.size() + 2 + 2 * prime.size(), -1);
		st.clear();
		int pos = 0;
		for(auto it = l.begin(); it != l.end(); it++, pos++){
			add(s, pos + 2, it ->second);
			add(pos + 2, s, 0);
			int g = it ->first;
			for(auto pme = prime.begin(); pme != prime.end() && *pme <= g; ++pme){
				if(g % *pme == 0){
					add(pos + 2, l.size() + r.size() + 2 + prime_loc[*pme], it ->second);
					add(l.size() + r.size() + 2 + prime_loc[*pme], pos + 2, 0);
					while(g % *pme)
						g /= *pme;
				}
			}
			if(g - 1){
				prime.insert(g);
				if(!prime_loc[g])
					prime_loc[g] = prime_loc.size();
				add(pos + 2, l.size() + r.size() + 2 + prime_loc[g], it ->second);
				add(l.size() + r.size() + 2 + prime_loc[g], pos + 2, 0);
			}
		}
		pos = 0;
		for(auto it = r.begin(); it != r.end(); it++, pos++){
			add(l.size() + 2 + pos, e, it ->second);
			add(e, l.size() + 2 + pos, 0);
			int g = it ->first;
			for(auto pme = prime.begin(); pme != prime.end() && *pme <= g; ++pme){
				if(g % *pme == 0){
					add(l.size() + r.size() + 2 + prime_loc[*pme], l.size() + 2 + pos, it ->second);
					add(l.size() + 2 + pos, l.size() + r.size() + 2 + prime_loc[*pme], 0);
					while(g % *pme == 0)
						g /= *pme;
				}
			}
			if(g - 1){
				prime.insert(g);
				if(!prime_loc[g])
					prime_loc[g] = prime_loc.size();
				add(l.size() + r.size() + 2 + prime_loc[g], l.size() + pos + 2, it ->second);
				add(l.size() + 2 + pos, l.size() + r.size() + 2 + prime_loc[g], 0);
			}
		}
		int res = 0;
		while(true){
			level.clear();
			level.resize(l.size() + r.size() + 2 * prime.size() + 2);
			level[s] = 1;
			queue<int> q;
			q.push(s);
			while(!q.empty()){
				int u = q.front();
				q.pop();
				for(int i = head[u]; ~i; i = st[i].nt){
					int v = st[i].v;
					int c = st[i].cap;
					if(level[v] || !c)
						continue;
					level[v] = level[u] + 1;
					q.push(v);
				}
			}
			if(!level[e])
				break;
			res += recur(s, e, INF);
		}
		cout << res << endl;
	}
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


E题

链接:https://www.nowcoder.com/acm/contest/130/E
来源:牛客网

题目描述

黑妹平时也很喜欢玩棋类游戏,但这次她在一块无限大的棋盘上玩游戏,她有n枚棋子分别在n个位置,她现在需要将其中k枚棋子移动到同一个位置组成一个超级棋子,棋子可以朝上下左右四个方向移动,
现在你需要告诉她最小的移动步数是多少?
注意同一时刻可以允许多枚棋子在同一个位置。

输入描述:

第一行一个整数T表示数据组数。(1 ≤ T ≤ 10)

对于每组数据:

第一行两个整数n和k,分别表示棋子的个数和组成超级棋子的棋子个数。(1 ≤ k ≤ n ≤ 100)

下面n行每行两个整数 xi,yi表示棋子的位置。(-109 ≤  xi, yi ≤ 109)

输出描述:

对于每组数据输出一行表示答案。

思路:

先思考这样一个问题,选择n颗棋子,最小的移动的是多少?

可以发现,最小移动点一定可以是他们之间的中点。

现在枚举每一个点作为中点,选择其中最优的。

O(n ^ 2 + nlogn)。


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

vll x, y;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	int times;
	cin >> times;
	while(times--){
		cin >> n >> k;
		x.clear();
		x.resize(n);
		y.clear();
		y.resize(n);
		for(int i = 0; i < n; i++)
			cin >> x[i] >> y[i];
		ll res = INF * INF;
		for(int i = 0; i < n; i++)
			for(int j = 0; j < n; j++){
				priority_queue<ll, vll, greater<ll> >q;
				for(int t = 0; t < n; t++)
					q.push(abs(x[t] - x[i]) + abs(y[t] - y[j]));
				ll cur = 0;
				for(int t = 0; t < k; t++){
					cur += q.top();
					q.pop();
				}
				res = min(res, cur);
			}
		cout << res << endl;
	}
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


F题

链接:https://www.nowcoder.com/acm/contest/130/F
来源:牛客网

题目描述

黑妹在玩游戏时又遇到了一个难题,这个游戏初始时给你k个整数x,黑妹需要构建一个长度为n的数组使得该数组恰好包含这k个整数,剩下的位置将由黑妹填上一些不大于100的正整数,要求数组里任意相邻的两个整数互素。
黑妹轻松解决了这个游戏,但是她想知道具体有多少种方法构建这个数组。
由于答案可能很大,输出这个数对1000000007取模后的值。

输入描述:

)

输出描述:

输出答案对1000000007取模后的值。


思路:

发现n较小且是填数游戏,考虑是否可以dp。

发现可以,dp[i][j][t]代表前i位,最后一位是j,已经选了t个x的总数。

转移方程呼之欲出。


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;

const int MAXN = 1e2 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

ll dp[MAXN][MAXN][MAXN];

int gcd(int a, int b){
	if(!b)
		return a;
	return gcd(b, a % b);
}

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	cin >> n >> m >> k;
	for(int i = 1; i <= 100; i++)
		if(i == m)
			dp[1][i][1] = 1;
		else
			dp[1][i][0] = 1;
	for(int i = 2; i <= n; i++)
		for(int j = 1; j <= 100; j++)
			for(int t = 0; t <= min(i - 1, k); t++)
				for(int x = 1; x <= 100; x++){
					if(gcd(x, j) > 1)
						continue;
					if(j == m)
						dp[i][j][t + 1] = (dp[i][j][t + 1] + dp[i - 1][x][t]) % MOD;
					else
						dp[i][j][t] = (dp[i][j][t] + dp[i - 1][x][t]) % MOD;
				}
	ll res = 0;
	for(int i = 1; i <= 100; i++)
		res = (res + dp[n][i][k]) % MOD;
	cout << res << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


未来可期。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值