校测 20241015 图论
保龄了!因为图论只自学过最短路
Problem 1. 礼物分配
为了庆祝大佬 \(wxh\) 的生日,众人决定为他准备礼物。现在有 \(n\) 个礼品盒排成一行,从 \(1\) 到 n 编号,每个礼品盒中可能有 \(1\) 个或 \(0\) 个礼品。大佬 \(wxh\) 提出了 \(m\) 个要求,形如“第 \(l[i]\) 到第 \(r[i]\) 个礼品盒当中至少有 \(c[i]\) 个礼品”。现在众人想知道,为了满足这些要求,所需准备的最少礼品数。
Input
第一行两个整数 \(n,m\),接下来 \(m\) 行每行三个整数 \(l[i],r[i],c[i]\)。
Output
一行一个整数代表答案。
Note
对于 \(30\%\) 的数据,\(n,m \leq 10\)。
对于 \(100\%\) 数据,\(n\leq1000,m\leq10000,1 \leq l[i],r[i] \leq n,0\leq c[i]\leq r[i]-l[i]+1\)
分析
差分约束。
把题目条件转化为对于前缀和 \(sum[r_i] - sum[l_i-1] \ge c_i\) ,即 \(sum[l_i-1]-sum[r]\le -c_i\) 建出对应边 \((r_i,l_i-1,-c_i)\) ;
注意到前缀和又另一组隐藏性质 \(0\le sum[i+1]-sum[i]\le1\) 建出对应边 \((i+1,i,0) 和 (i,i+1,1)\);
最后再连出从超级源点出发的各点 \((s,i,0)\);
建完图直接SPFA跑最短路,输出 \(sum[n]-sum[0]\) 即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
const int maxn = 1e3 + 10, maxm = 1e4 + 10;
struct edge{
int v, nxt, w;
}e[maxm << 1];
int head[maxm], tot = 0;
void add(int u, int v, int w) {
e[++tot].nxt = head[u];
head[u] = tot;
e[tot].v = v;
e[tot].w = w;
}
queue<int> q;
int s, vis[maxn], d[maxn];
void spfa() {
memset(d, 0x3f, sizeof d);
d[s] = 0;
vis[s] = 1;
q.push(s);
while(q.size()) {
int u = q.front(); q.pop();
vis[u] = 0;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v, w = e[i].w;
if(d[u] + w < d[v]) {
d[v] = d[u] + w;
if(!vis[v]) q.push(v), vis[v] = 1;
}
}
}
return;
}
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
int n = read(), m = read();
for(int i = 1; i <= m; i++) {
int l = read(), r = read(), c = read();
add(r, l - 1, -c);
}
for(int i = 0; i <= n; i++) {
add(i + 1, i, 0); add(i, i + 1, 1);
}
s = n + 1;
for(int i = 1; i <= n; i++) add(s, i, 0);
spfa();
printf("%d", d[n] - d[0]);
return 0;
}
Problem 2. 重量差异
在实验室中,Nathan Wada 作为助手的职责是测定两个样品的重量差异。当样品的差异很小时,使用天平比使用弹簧秤能得到更精确的结果,所以 Nathan Wada 只使用天平来测得一些样品的重量差。Nathan Wada 偶尔会被询问一些样品的重量差,而他能否回答这些问题取决于在回答相应问题时他已经得到的测量结果。由于 Nathan Wada 所要处理的测量数据是巨大的,所以他希望你能写个程序帮他处理数据和回答问题。
Input
第一行包含两个整数 \(N\) 和 \(M\) ,其中 \(N\) 表示样品的数量,样品从 \(1\) 到 \(N\) 标号。
接下来 \(M\) 行,每行包括一个测量结果或者询问,按时间顺序给出。
一个测量结果被格式化为 \(! \ a \ b \ w\) ,表示第 \(a\) 个样品比第 \(b\) 个样品轻 \(w\) 个单位重量,且任意的测试结果互不矛盾。
一个询问被格式化为 \(? \ a \ b\) ,表示询问第 \(a\) 个样品比第 \(b\) 个样品轻多少个单位重量。
Output
对于每个询问输出一行,如果能回答问题,则输出问题的答案,你可以认为答案的绝对值不超过 \(1000000\) 。否则输出 "UNKNOWN" ,表示不能回答问题。
Note
对于 \(30\%\) 的数据,\(N,M\le1000\)。
对于 \(100\%\) 数据, \(1\le N,M \le 100000,1\le a,b\le N\)。
分析
注意到给定的关系形成一棵树,带权并查集维护即可
AC代码:
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
const int maxn = 1e5 + 10;
int fa[maxn], val[maxn];
int get(int u) {
if(fa[u] == u) return u;
int rt = get(fa[u]);
val[u] += val[fa[u]];
return fa[u] = rt;
}
int main() {
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
int n = read(), m = read();
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= m; i++) {
char op = getchar();
if(op == '!') {
int u = read(), v = read(), w = read();
int x = get(u), y = get(v);
if(x != y) fa[x] = fa[y], val[x] = w + val[v] - val[u];
}
if(op == '?') {
int u = read(), v = read();
int x = get(u), y = get(v);
if(x != y) printf("UNKNOWN\n");
else printf("%d\n", val[u] - val[v]);
}
}
return 0;
}
Problem 3. 车站分级
Note
对于 \(10\%\) 的数据,\(n,m\le100\)。
对于 \(40\%\) 的数据,\(n,m\le5000\)。
对于 \(100\%\) 的数据,\(n,m<=100000\) , \(\sum s_i\le100000\)。
分析
数据范围扩大了 \(100\) 倍。
说是线段树优化建图。我不会原题所以不写了