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;
}
posted @ 2020-10-17 21:58  C锥  阅读(157)  评论(1编辑  收藏  举报