导读:
简单的题目,只说明题意,或者直接说明结论
下面的难题,会做详细的解释和证明
A 黑白边
题意:n个点,m条边,每条边连接两个点,边权值有0,有1,问若图不连通,输出-1,否则,输出最少用多少个1
这就很显然了,只需要用kruskal跑一遍就OK了,因为0边在前,1边在后
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
struct Node{
int x, y, z;
bool operator< (const Node &W) const{
return z < W.z;
}
}e[N];
int n, m;
int fa[N];
int get(int x)
{
return fa[x] == x ? x : fa[x] = get(fa[x]);
}
void kruskal()
{
int cnt = 0, ct = 0;
for (int i = 0; i < m; i ++ )
{
int x = e[i].x, y = e[i].y, z = e[i].z;
x = get(x), y = get(y);
if (x == y) continue;
fa[x] = y;
cnt ++;
if (z) ct ++;
}
if (cnt < n - 1) cout << "-1" << "\n";
else cout << ct << "\n";
}
int main(void)
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) fa[i] = i;
for (int i = 0; i < m; i ++ )
{
int x, y, z; scanf("%d%d%d", &x, &y, &z);
e[i] = {x, y, z};
}
sort(e, e + m);
kruskal();
return 0;
}
B 最好的宝石
单值修改、区间查询,只有pushup的线段树
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, m;
int w[N];
struct Node{
int l, r;
int mx, cnt;
}tr[N * 4];
struct Seg_Tree{
void pushup(Node &c, Node &a, Node &b)
{
if (a.mx == b.mx) c.cnt = a.cnt + b.cnt;
else if (a.mx > b.mx) c.cnt = a.cnt;
else c.cnt = b.cnt;
c.mx = max(a.mx, b.mx);
}
void pushup(int u)
{
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r)
{
if (l == r)
{
tr[u] = {l, r, w[l], 1};
return;
}
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void change(int u, int x, int c)
{
if (tr[u].l == x && tr[u].r == x)
{
tr[u] = {x, x, c, 1};
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) change(u << 1, x, c);
if (x > mid) change(u << 1 | 1, x, c);
pushup(u);
}
Node query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) return query(u << 1, l, r);
else if (l > mid) return query(u << 1 | 1, l, r);
else
{
Node left = query(u << 1, l, r);
Node right = query(u << 1 | 1, l, r);
Node ans;
pushup(ans, left, right);
return ans;
}
}
}t;
int main(void)
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
t.build(1, 1, n);
char op[10];
int l, r;
while (m -- )
{
scanf("%s", op);
scanf("%d%d", &l, &r);
if (*op == 'C')
{
t.change(1, l, r);
}
else
{
Node x = t.query(1, l, r);
printf("%d %d\n", x.mx, x.cnt);
}
}
return 0;
}
C 滑板上楼梯
一看到楼梯,就想到dp的状态机模型,额,但是一看n是1e12就给放弃了,在思考一下,贪心模拟给过了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
int main()
{
cin >> n;
cout << n / 4 * 2 + (n % 4 == 3 ? 1 : n % 4) << endl;
return 0;
}
D GCD
我们贪心的考虑,如果这个子集包含了1~n中全部的素数呢(因为是任意问题,所以考虑最坏情况),很显然gcd(xi,xj) = 1,在加上一个1呢,还是gcd(xi,xj) = 1,但是我们要是往里面在加一个数,那么这个数一定是合数,这个合数加入了集合,一定会和某个素数是倍数关系,那么一定合法。
所以有如下代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n;
int p[N], cnt;
bool st[N];
void get_primes()
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) p[cnt ++ ] = i;
for (int j = 0; i * p[j] <= n; j ++ )
{
st[i * p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
int main()
{
cin >> n;
get_primes();
cout << (cnt + 2 > n ? -1 : cnt + 2) << endl;
return 0;
}
牛牛的加法
模拟,注意要去掉前导0
#include <bits/stdc++.h>
using namespace std;
string a, b, c;
int main()
{
cin >> a >> b;
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
for (int i = 0; i < a.size() || i < b.size(); i ++ )
{
int t = 0;
if (i < a.size()) t = a[i] - '0';
if (i < b.size()) t += b[i] - '0';
c += t % 10 + '0';
}
reverse(c.begin(), c.end());
while (c.front() == '0' && c.size() > 1) c.erase(0, 1);
cout << c << endl;
return 0;
}
F- 石子合并
贪心的考虑,因为没有合并的过程,每次都是要从相邻两堆中那走较轻的一堆,但所得的成绩是两堆之和,那么贪心的考虑每次加上max的一堆。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200005;
ll a[N];
int main()
{
int n; cin >> n;
ll mx = 0;
ll sum = 0;
for (int i = 1; i <= n; i ++ )
{
scanf("%lld", &a[i]);
mx = max(mx, a[i]);
sum += a[i];
}
cout << sum + mx * (n - 2) << endl;
return 0;
}
G-滑板比赛
双指针呗,没啥好说的
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, m;
int a[N], b[N];
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
for (int i = 0; i < m; i ++ ) scanf("%d", &b[i]);
sort(a, a + n);
sort(b, b + m);
// for (int i = 0; i < n; i ++ ) printf("%d ", a[i]); puts("");
// for (int i = 0; i < m; i ++ ) printf("%d ", b[i]); puts("");
int cnt = 0;
for (int j = 0, i = 0; j < m; j ++ )
{
while (a[i] <= b[j] && i < n) i ++;
// printf("i = %d, a[i] = %d, j = %d, b[j] = %d\n", i, a[i], j, b[j]);
if (a[i] > b[j])cnt ++;
i ++;
}
cout << cnt << "\n";
return 0;
}