第十一届山东省大学生程序设计竞赛部分题解
B
思路
如果给出的点权里面有素数,由于素数仅能被1和其自身整除,因此所有和点权为素数的点相连的所有边的边权大小均为1,所以最小生成树的权值为n-1。当点的数量特别多的时候,可以认为点集里一定存在素数;当点的数量少的时候直接使用暴力算法即可。
代码
#include <bits/stdc++.h>
using namespace std;
int n, L, R, a[200001];
unsigned long long seed;
unsigned long long xorshift64() {
unsigned long long x = seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return seed = x;
}
int gen() {
return xorshift64() % (R-L+1)+L;
}
const int maxn = 2e5+5;
struct Edge {
int u, v, w;
} edges[maxn*10];
int tot = -1;
int fa[maxn];
int find(int i) {
if(fa[i] != i) {
fa[i] = find(fa[i]);
}
return fa[i];
}
void merge(int i, int j) {
int f1 = find(i);
int f2 = find(j);
fa[f1] = f2;
}
void init(int n) {
for(int i = 0; i <= n+4; ++i) {
fa[i] = i;
}
}
void add(int u, int v, int w) {
++tot;
edges[tot].u = u;
edges[tot].v = v;
edges[tot].w = w;
}
bool cmp(Edge& a, Edge& b) {
return a.w < b.w;
}
int Kruskral(int n) {
init(n);
sort(edges, edges+tot+1, cmp);
int p, q, ans = 0, cnt = 0;
for(int i = 0; i <= tot; ++i) {
p = find(edges[i].u);
q = find(edges[i].v);
if(q != p) {
ans += edges[i].w;
fa[p] = q;
++cnt;
}
if(cnt == n-1) return ans;
}
return ans;
}
int main(void)
{
scanf("%d%d%d%llu", &n, &L, &R, &seed);
for(int i = 1; i <= n; ++i) {
a[i] = gen();
}
if(L == R) printf("%lld", (n-1)*1ll*L); // L=R的情况需要特判
else if (n >= 20) printf("%lld", n-1);
else {
for(int i = 1; i <= n; ++i) {
for(int j = i+1; j <= n; ++j) {
add(i, j, __gcd(a[i], a[j]));
}
}
printf("%lld", Kruskral(n));
}
return 0;
}
C
思路
当只有一个点时,染色的可能数有两个。如果有多个点,根节点有左子树也有右子树,这这个树的染色的可能数为:左子树的可能数*右子树的可能数 + 1;如果根节点只有一个子树,则这个数的染色数为:子树的染色数+1 。这样就可以看到明显的递归结构。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PLL;
const ll maxn = 1e5+5;
PLL tree[maxn];
ll tot = -1;
ll cur = 1;
void build(ll x, ll fa) { // 建造染色数为x,根节点为fa的一个数
if(x == 2) return;
else if(x == 3) {
tree[++tot] = {fa, ++cur};
build(x-1, cur);
} else if(x&1) {
tree[++tot] = {fa, ++cur};
build(2, cur);
tree[++tot] = {fa, ++cur};
build((x-1)/2, cur);
} else {
tree[++tot] = {fa, ++cur};
build(x-1, cur);
}
}
int main(void)
{
ll n;
scanf("%lld", &n);
build(n, 1);
printf("%lld\n", cur);
for(int i = 0; i <= tot; ++i) {
printf("%lld %lld\n", tree[i].first, tree[i].second);
}
return 0;
}
D
思路
先拿在竖直方向施加重力为例:每次记录新添加的块放在哪一行,如果新添加的块是这一列的第一行,则先向边数里面加4,如果不是新添加的块,则先向边数里面加2;再看新添加的块是否和左右的列有相邻的块,每有一个相邻的块就在总边数里面减2。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
int n, v[maxn], h[maxn];
int main(void)
{
int x, y;
memset(v, 0, sizeof(v));
memset(h, 0, sizeof(h));
scanf("%d", &n);
ll ansv = 0, ansh = 0;
for(int i = 0; i < n; ++i) {
scanf("%d%d", &x, &y);
++v[x];
++h[y];
if(v[x] == 1) {
ansv += 4;
} else {
ansv += 2;
}
if(v[x] <= v[x-1]) {
ansv -= 2;
}
if(v[x] <= v[x+1]) {
ansv -= 2;
}
if(h[y] == 1) {
ansh += 4;
} else {
ansh += 2;
}
if(h[y] <= h[y-1]) {
ansh -= 2;
}
if(h[y] <= h[y+1]) {
ansh -= 2;
}
printf("%lld %lld\n", ansv, ansh);
}
return 0;
}
G
思路
模拟除法。
代码
#include <bits/stdc++.h>
using namespace std;
int main(void)
{
int n, k;
scanf("%d%d", &n, &k);
int sum = 0, x;
for(int i = 0; i < n; ++i) {
scanf("%d", &x);
sum += x;
}
int left = sum;
for(int i = 0; i < k+1; ++i) {
if(i == 1) printf(".");
if(left) {
printf("%d", left/n);
left %= n;
left *= 10;
} else {
printf("0");
}
}
return 0;
}
H
思路
背包问题,多了一个维度。如果开三维数组会爆,所以应该采用优化方案。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1005;
ll dp[maxn][maxn];
ll h[maxn], s[maxn], w[maxn];
int main(void)
{
int n, H, S;
memset(dp, 0, sizeof(dp));
scanf("%d%d%d", &n, &H, &S);
for(int i = 1; i <= n; ++i) {
scanf("%lld%lld%lld", &h[i], &s[i], &w[i]);
}
for(int i = 1; i <= n; ++i) {
for(int j = H; j > h[i]; --j) {
for(int k = S; k >= 0; --k) {
if(k-s[i]<0) {
if(j-h[i]-(s[i]-k) <= 0) continue;
dp[j][k] = max(dp[j-h[i]-(s[i]-k)][0]+w[i], dp[j][k]);
} else {
dp[j][k] = max(dp[j-h[i]][k-s[i]]+w[i], dp[j][k]);
}
}
}
}
printf("%lld", dp[H][S]);
return 0;
}
M
思路
照着题解看的,题解写的非常通俗易懂,可惜当时没看这个题😭。
代码
#include <cstdio>
using namespace std;
const int maxn = 505;
int arr[maxn][maxn];
int main(void)
{
// freopen("in.txt", "r", stdin);
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0 ;i < n; ++i) {
for(int j = 0; j < m; ++j) {
scanf("%1d", &arr[i][j]);
}
}
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
if(j == 0) {
printf("1");
} else if(!(i&1) && j != m-1) {
printf("1");
} else {
printf("%d", arr[i][j]);
}
}
printf("\n");
}
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
if(j == m-1) {
printf("1");
} else if((i&1) && j != 0) {
printf("1");
} else {
printf("%d", arr[i][j]);
}
}
printf("\n");
}
return 0;
}