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
来源:牛客网
题目描述
游戏从左上角开始右下角结束,双方交替的选择一个方格并获得方格上相应的分数,一方选择的方格必须在上一步另一方选择的方格
的右边或者下面,黑妹先开始。现在黑妹想知道,如果双方都采取最优策略(最优策略是指双方都希望最终自己的总分数减去对方的总分数最大),她的总分数减去黑弟的总分数会是多少?
输入描述:
第一行一个整数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
来源:牛客网
题目描述
选择满足以下条件的两个序对(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
来源:牛客网
题目描述
输入描述:
第一行一个整数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
来源:牛客网
题目描述
输入描述:
)
输出描述:
输出答案对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;
}
未来可期。