团队分组
题面
在 Farmer John 最喜欢的节日里,他想要给他的朋友们赠送一些礼物。由于他并不擅长包装礼物,他想要获得他的奶牛们的帮助。你可能能够想到,奶牛们本身也不是很擅长包装礼物,而 Farmer John 即将得到这一教训。
Farmer John 的 \(N\) 头奶牛(\(1\le N\le 10^4\)
)排成一行,方便起见依次编号为 \(1\dots N\)。奶牛 \(i\) 的包装礼物的技能水平为 \(s_i\)
。她们的技能水平可能参差不齐,所以 FJ 决定把她的奶牛们分成小组。每一组可以包含任意不超过 K头的连续的奶牛(\(1\le K\le 10^3\)),并且一头奶牛不能属于多于一个小组。由于奶牛们会互相学习,这一组中每一头奶牛的技能水平会变成这一组中水平最高的奶牛的技能水平。
请帮助 FJ 求出,在他合理地安排分组的情况下,可以达到的技能水平之和的最大值。
题解
dp裸题,用\(f(i)\)表示前i头奶牛的最大水平之和。
则\(f(i)=max\{f(j-1)+Max[j...i]\times (i-j+1)\}\)这个方程的转移时间复杂度是\(O(k)\),那么总时间复杂度应该是\(O(nk)\),然后Max的求值有一点小技巧——从小到大。
Main Code:
for (int i = 1; i <= n; i++) {
int arg = 0;
for (int j = i; j >= 1 && j > i - k; j--) {
arg = max(arg, a[j]);
f[i] = max(f[i], f[j - 1] + arg * (i - j + 1));
}
}
和谐
题面
研究证明,有一个因素在两头奶牛能否作为朋友和谐共处这方面比其他任何因素都来得重要——她们是不是喜欢同一种口味的冰激凌!
Farmer John 的 NN 头奶牛(\(2\le N\le 5\times 10^4\))各自列举了她们最喜欢的五种冰激凌口味的清单。为使这个清单更加精炼,每种可能的口味用一个不超过 \(10^6\)
的正整数 \(\texttt{ID}\) 表示。如果两头奶牛的清单上有至少一种共同的冰激凌口味,那么她们可以和谐共处。
请求出不能和谐共处的奶牛的对数。
题解
这里可以使用容斥原理,用总对数\(n\times (n-1) \over 2\)减去和谐的对数。现在的关键是,怎么计算和谐对数。这里我们要使用\(C++\)的\(map\)(Pascal的dalao对不起了),首先对于一头奶牛用五元组\((a_1, a_2, a_3, a_4, a_5)\)表示。我们看看在这头奶牛之前,有多少头奶牛可以与他和谐相处,首先加上有一个口味相同的奶牛数,接着减去有2个口味相同的奶牛数,再加上3个口味相同的奶牛数。。。(都是容斥的基本操作)
思路没什么问题,但是我的实现实在是太zz了,大数据点都是卡过去的,勉强着看吧:
code
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int t = 0;
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while('0' <= ch && ch <= '9')
t = (t << 3) + (t << 1) + ch - '0',
ch = getchar();
return t;
}
const int maxn = 5e4 + 5;
struct Note {
int num[5];
friend bool operator < (Note a, Note b) {
for (int i = 0; i < 5; i++) if (a.num[i] != b.num[i])
return a.num[i] < b.num[i];
return false;
}
void print() {
for (int i = 0; i < 5; i++) printf("%d\t", num[i]);
}
} a[maxn];
int n, cnt_arr[1 << 5]; long long ans;
Note tmp[1 << 5];
map<Note, long long> sum;
inline int Cnt(int S) {
int ret = 0;
for (int i = 0; i < 5; i++) if (S & (1 << i)) ret++;
return ret;
}
int main() {
n = read();
int i, j, S, p;
ans = 1ll * n * (n - 1) / 2ll;
for (i = 1; i <= n; ++i) {
for (j = 0; j < 5; ++j) a[i].num[j] = read();
sort(a[i].num, a[i].num + 5);
}
for (S = 1; S < (1 << 5); ++S) cnt_arr[S] = Cnt(S);
int maxS = 1 << 5;
for (i = 1; i <= n; ++i) {
for (S = 1; S < maxS; ++S) {
p = 0;
for (j = 0; j < 5; ++j) if (S & (1 << j))
tmp[S].num[p++] = a[i].num[j];
ans += ((cnt_arr[S] & 1) ? -1 : 1) * sum[tmp[S]];
}
for (S = 1; S < maxS; ++S) sum[tmp[S]]++;
}
printf("%lld\n", ans);
return 0;
}
美味
题面
漫长的一天结束了,饥困交加的奶牛们准备返回牛棚。
农场由 NN 片牧场组成(\(2\le N\le 5\times 10^4\)),方便起见编号为 \(1\dots N\)。所有奶牛都要前往位于牧场 NN 的牛棚。其他 N−1片牧场中每片有一头奶牛。奶牛们可以通过 MM 条无向的小路在牧场之间移动(\(1\le M\le 10^5\))。第i条小路连接牧场 \(a_i\)和 \(b_i\),通过需要时间 \(t_i\) 。每头奶牛都可以经过一些小路回到牛棚。
由于饥饿,奶牛们很乐于在他们回家的路上停留一段时间觅食。农场里有 \(K\) 个有美味的干草捆,第 \(i\) 个干草捆的美味值为 \(y_i\)
。每头奶牛都想要在她回牛棚的路上在某一个干草捆处停留,但是她这样做仅当经过这个干草捆使她回牛棚的时间增加不超过这个干草捆的美味值。注意一头奶牛仅仅“正式地”在一个干草捆处因进食而停留,即使她的路径上经过其他放有干草捆的牧场;她会简单地无视其他的干草捆。
# 题解
这道题使用SPFA以及有后效性的dp方程完成。
首先,使用最短路算法求出每个点到点n的最短路,记为\(dis(u)\)
接着,用\(f(i)\)表示点i吃了草之后到达点n的最短时间(减去\(y_i\))显然,对于有干草堆的点,他们的\(f(i)=dis(i)-y_i\),接着,对于其他的点,\(f(u)=min\{f(v)+w(u,v)\}\)
但是,由于某种原因,这种方程是具有后效性,但是值是可以用spfa求出的。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
using namespace std;
const int maxn = 5 * 1e4 + 5, maxm = 1e5 + 5;
struct Edge {
int v, w, nex;
Edge() {}
Edge(int _v, int _w, int _nex) : v(_v), w(_w), nex(_nex) {}
} E[maxm << 1];
int head[maxn], tote;
void addEdge(int u, int v, int w) {
E[++tote] = Edge(v, w, head[u]), head[u] = tote;
E[++tote] = Edge(u, w, head[v]), head[v] = tote;
}
int n, m, k, y[maxn];
int dis[maxn], vis[maxn];
priority_queue< pair<int, int> > q;
queue<int> qq;
void DIJ() {
for (int i = 1; i <= n; i++) dis[i] = 1e9;
q.push(make_pair(dis[n] = 0, n));
while (!q.empty()) {
int u = q.top().second; q.pop();
if (vis[u]) continue;
vis[u] = true;
for (int i = head[u]; ~i; i = E[i].nex) {
int v = E[i].v;
if (dis[v] > dis[u] + E[i].w)
dis[v] = dis[u] + E[i].w,
q.push(make_pair(-dis[v], v));
}
}
// for (int i = 1; i <= n; i++) printf("%d ", dis[i]);
// putchar('\n');
}
int dp[maxn];
void DP() {
for (int i = 1; i <= n; i++) dp[i] = 99999999;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++) if (y[i]) {
qq.push(i), dp[i] = dis[i] - y[i], vis[i] = true;
// printf("push %d\n", i);
}
while(!qq.empty()) {
int u = qq.front(); qq.pop(), vis[u] = false;
// printf("pop %d\n", u);
for (int i = head[u]; ~i; i = E[i].nex) {
int v = E[i].v;
if (dp[v] > dp[u] + E[i].w) {
dp[v] = dp[u] + E[i].w;
if (!vis[v]) vis[v] = true, qq.push(v);
}
}
}
// for (int i = 1; i <= n; i++) printf("%d ", dp[i]);
// putchar('\n');
}
int main() {
memset(head, -1, sizeof(head));
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addEdge(u, v, w);
}
for (int i = 1; i <= k; i++) {
int u, val;
scanf("%d%d", &u, &val);
y[u] = max(y[u], val);
}
DIJ(), DP();
for (int i = 1; i < n; i++)
if (dp[i] <= dis[i]) printf("1\n");
else printf("0\n");
return 0;
}