1016考试总结
1016考试总结
T1
sb题????
T2
题目大意:
给定一张有向图,每个点有点权。试找到一条路径,使得该路径上的点权最大值减去点权最小值最大,问这个差最大是多少。对于100%的数据,1≤𝑁≤105,1≤𝑀≤5×10 ^ 5,点权不超过10^6。
bfs。
考场上想的Tarjan缩点,然后拓扑维护每个点经过路径的最大值和最小值,拿了60pts,这样是错误做法:因为可能不在同一条路径上的最大值最小的被弄到一个点上了,就错了。
正解:按点权从小到大排个序,正反变都要建,每次\(bfs\)这个点所能到的点,并且把到的点的最小值赋成起点,最后再遍历每个点,算最大差值。貌似不难,但就是没有想到,可能是没仔细想第一个思路吧。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5, M = 5e5 + 5;
int n, m, cnt, ans;
int a[N], id[N], head[N], f_min[N];
struct edge { int f, to, nxt; } e[M << 1];
void add(int x, int y) {
e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].f = 1;
e[++ cnt].nxt = head[y]; head[y] = cnt; e[cnt].to = x; e[cnt].f = 0;
}
int cmp(int x, int b) { return a[x] < a[b]; }
void bfs(int x) {
if(!f_min[x]) f_min[x] = a[x];
queue <int> q; q.push(x);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; i ; i = e[i].nxt) {
if(e[i].f == 1 || f_min[e[i].to]) continue;
f_min[e[i].to] = a[x]; q.push(e[i].to);
}
}
q.push(x);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; i ; i = e[i].nxt) {
if(e[i].f == 0 || f_min[e[i].to]) continue;
f_min[e[i].to] = a[x]; q.push(e[i].to);
}
}
}
int main() {
n = read(); m = read();
for(int i = 1;i <= n; i++) a[i] = read(), id[i] = i;
for(int i = 1, x, y;i <= m; i++) x = read(), y = read(), add(x, y);
sort(id + 1, id + n + 1, cmp);
for(int i = 1;i <= n; i++) bfs(id[i]);
for(int i = 1;i <= n; i++) ans = max(ans, a[i] - f_min[i]);
printf("%d", ans);
fclose(stdin); fclose(stdout);
return 0;
}
T3
题目大意:
有𝑁个人,每个人都有两把刷子,每个刷子都有一个属性值。如果说一个人拿着的两把刷子的属性值之差的绝对值超过了𝑐,则这个人无法使用他的两把刷子。现在你可以选择交换不同人的某把刷子,使得每个人都能够使用他们的刷子,问最小所需要的交换次数。对于100%的数据,1≤𝑁≤16,𝑐≤10^ 6且属性值也不超过10^6。
缩索 + 剪枝。
std代码是状压DP,我太菜了看不懂。考场上写的缩索,加了几个剪枝竟然过了,跑的还比std快,就很迷。
将每个人的刷子先变成小的是第一个,大的是第二个,然后按第一个为第一关键字排序,第二个为第二关键字从小到大排序。然后缩索就好了。为啥要排序呢?
因为我写了一个剪枝:\(if(a[now].x == a[now].y) { dfs(now + 1, step); }\),不排序的话这种数据可能会挂:
2 2
5 5
3 7
正确答案应该是1,但是不排序的话就会无解。为什么排序一定可以避免这种情况呢?
我们设某一个数据是这样的:
i : x x
i + 1 : y z (y <= z)
// y 与 z 不合法 x 与 y 合法, x 与 z 合法
如果说\(y <= x <= z\),那么一定可以通过排序使"y z"提前,避免无解;如果\(x <= y <= z\),那么\(z - x >= z - y\),又因为\(z - x <= c\),那么\(z - y <= c\),与\(y\)和\(z\)不合法矛盾;\(y <= z <= x\)同理。
还有一个判断无解的函数,我们把所有刷子从小到大排序,然后两个两个的比较,如果其中的一组不和法,那么一定无解:
\(...x_i, x_{i + 1}...\),因为已经从小到大排序,并且\(x_{i + 1} - x_i > c\),那么\(x_{i + 1}\)减去\(x_i\)前面的肯定大于c,\(x_{i + 1}\)后边的减去\(x_i\)也肯定大于c,所以无解。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 20, inf = 1e9;
int n, c, ans, tag, b[N << 1];
struct cj { int x, y; } a[N];
int cmp(cj a, cj b) {
if(a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
int judge() { // 判断是否无解
int cnt = 0;
for(int i = 1;i <= n; i++) b[++ cnt] = a[i].x, b[++ cnt] = a[i].y;
sort(b + 1, b + cnt + 1);
for(int i = 1;i <= cnt; i += 2) if(b[i + 1] - b[i] > c) return 0;
return 1;
}
void dfs(int now, int step) {
if(step >= ans) { return ; }
if(now == n + 1) { ans = min(ans, step); return ; }
if(a[now].x == a[now].y) { dfs(now + 1, step); }
else {
if(abs(a[now].x - a[now].y) <= c) {
dfs(now + 1, step);
}
for(int i = now + 1;i <= n; i++) {
if(abs(a[i].y - a[now].x) <= c) {
swap(a[i].y, a[now].y);
dfs(now + 1, step + 1);
swap(a[i].y, a[now].y);
}
if(abs(a[i].x - a[now].x) <= c) {
swap(a[i].x, a[now].y);
dfs(now + 1, step + 1);
swap(a[i].x, a[now].y);
}
}
}
}
int main() {
n = read(); c = read(); ans = inf;
for(int i = 1;i <= n; i++) {
a[i].x = read(), a[i].y = read();
if(a[i].x > a[i].y) swap(a[i].x, a[i].y);
if(abs(a[i].x - a[i].y) > c) tag = 1;
}
sort(a + 1, a + n + 1, cmp);
if(!tag) printf("0");
else {
if(judge()) {
dfs(1, 0);
printf("%d", ans);
}
else {
printf("-1");
}
}
return 0;
}
T4
题目大意:
题目链接
其实原题和洛谷上这个题有点不同,洛谷上是对区间每个\(a_i\)加上\(f_{i−l+1}\),原题是加上\(f_{i−l+1 + x}\),多了个输入的\(x\),我现在还太弱,不会做,只能做做洛谷上这道类似的了。
首先要知道广义斐波那契数列:\(s_i = a * f_{i - 1} + b *f_{i - 2}, a = s1, b = s2\)。对于合并两个广义斐波那契数列,只需将他们各自的\(a, b\)相加即可,证明我不会。
我们每次区间加了个这个:\(\displaystyle \sum_{i = 1}^{n} f_i\),化简一下就是\(f_1 + f_2 + f_2 +f_3 + f_4 + f_5 + ... +f _ n - f_2= f_{n + 2} - f_2\)。
所以我们可以线段树维护广义斐波那契数列的\(a, b\)就好了,就是搞两个懒标记。
区间和:\(\displaystyle \sum_{i = 1}^{n} s_i = s_{n + 2} -s_2 = a * f_{n + 1} + b *f_{n} - b\)。
#include <bits/stdc++.h>
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 3e5 + 5, mod = 1e9 + 9;
int n, m, f[N << 1];
struct tree { int tag1, tag2, sum, len; } t[N << 2];
void make_pre_f() {
f[1] = 1; f[2] = 1;
for(int i = 3;i < (N << 1); i++) f[i] = (f[i - 1] + f[i - 2]) % mod;
}
void up(int o) {
t[o].sum = (t[ls(o)].sum + t[rs(o)].sum) % mod;
}
void build(int o, int l, int r) {
t[o].len = r - l + 1;
if(l == r) { t[o].sum = read(); return ; }
build(ls(o), l, mid); build(rs(o), mid + 1, r);
up(o);
}
void modify(int o, int l, int r) {
t[o].tag1 = (t[o].tag1 + l) % mod; t[o].tag2 = (t[o].tag2 + r) % mod;
t[o].sum = (t[o].sum + (1ll * l * f[t[o].len] % mod + 1ll * r * f[t[o].len + 1] % mod) % mod - r + mod) % mod;
}
void down(int o) {
if(!t[o].tag1 && !t[o].tag2) return ;
int a = ((1ll * t[o].tag1 * f[t[ls(o)].len - 1] % mod) + (1ll * t[o].tag2 * f[t[ls(o)].len] % mod)) % mod;
int b = ((1ll * t[o].tag1 * f[t[ls(o)].len] % mod) + (1ll * t[o].tag2 * f[t[ls(o)].len + 1] % mod)) % mod;
modify(ls(o), t[o].tag1, t[o].tag2); modify(rs(o), a, b);
t[o].tag1 = t[o].tag2 = 0;
}
void change(int o, int l, int r, int x, int y) {
if(x <= l && y >= r) { modify(o, f[l - x + 1], f[l - x + 2]); return ; }
down(o);
if(x <= mid) change(ls(o), l, mid, x, y);
if(y > mid) change(rs(o), mid + 1, r, x, y);
up(o);
}
int query(int o, int l, int r, int x, int y) {
if(x <= l && y >= r) { return t[o].sum; }
down(o);
int res = 0;
if(x <= mid) res = (res + query(ls(o), l, mid, x, y)) % mod;
if(y > mid) res = (res + query(rs(o), mid + 1, r, x, y)) % mod;
return res;
}
int main() {
n = read(); m = read();
make_pre_f();
build(1, 1, n);
for(int i = 1, opt, l, r;i <= m; i++) {
opt = read(); l = read(); r = read();
if(opt == 1) { change(1, 1, n, l, r); }
if(opt == 2) { printf("%d\n", query(1, 1, n, l, r)); }
}
return 0;
}