省选测试12

省选测试 12

T1

​ 给定一个长度为\(n\)的数列, 第\(i\)个数字是\(a_i\). 从中任意选一些数字, 那么选出这些数字的分数就是 : 所有满足\(1 \leq L \leq R \leq N\)且区间\([L,R]\)中的数字全部被选中的数对\((L,R)\)的个数 - 被选中的数字之和.

​ 现有\(m\)次询问, 每次询问相互独立, 每次询问给定两个参数\(p,x\), 表示要把\(a_p\)改为\(x\), 问修改之后可以获得的最大分数是多少.

\(n, m \leq 3e5\).

​ 这题调的我真是ex.

​ 斜率优化dp + 分治.

​ 首先最简单的\(dp\)式应该可以想到, 设\(f_i\)为前\(i\)个数的最大得分, 那么转移方程就是 :

\[f_i = max(f_{j-1} + fab(i-j) - (sum_i - sum_{j-1})) \]

\(fab(x) = \frac{x*(x+1)}{2}\), \(sum_i\)代表前\(i\)个数字的和.

​ 有了这个时候我们发现可以拆开优化, 那我们把它拆开看一看 :

\[f_i = max(f_{j-1} + sum_{j-1} + \frac{j^2}{2} - \frac{j}{2} - ij) - sum_i +\frac{i^2}{2}+\frac{i}{2} \]

​ 如果我们把\((j, f_{j-1} + sum_{j-1} + j^2 - j)\)看成二维平面上的点的话, 那我们就可以用斜率优化了.为了避免实数运算, 我们把所有都乘2. 再变换一下更清晰的式子 :

\[2ij + 2f_i - (i^2+i - 2sum_i) = 2f_{j-1} + 2sum_{j-1} + j^2 - j \]

​ 然后斜率优化就好了.

​ 但这只是没有询问的做法, 有询问怎么办呢?

​ 还是要用斜率优化, 只不过加了个分治.

​ 设\(f_i\)为前\(i\)个数字的最大分数, \(g_i\)为后\(i\)个数字的最大分数, 这两个用上面的方法即可求出.

​ 设\(c_i\)为必须选\(i\)这个数字的最大分数.

​ 那么对于一个询问\(a_p\)修改成\(x\)的答案就是 : \(max(f_{p-1} + g_{p+1}, c_p+a_p-x)\).

​ 那么怎么求出来\(c_i\)呢? 考虑分治, 假如说当前的分治区间是\([l,r]\).

​ 我们设\(R_i\)为从右部选一个位置\(i\), 左部选任意选位置\(j\), 区间\([j,i]\)必须选的最大分数是多少. 这个\(R_i\)将会对区间\([mid+1, i]\)位置的\(c\)造成贡献.同理, 把\(L_i\)设成从左部选一个位置\(i\), 从右部任意选位置\(j\), 区间\([i,j]\)必须选的最大分数是多少.\(L_i\)将会对区间\([i,mid]\)\(c\)造成贡献.(因为这些\(c\)一定是在\(L_i\)的区间范围内的)

​ 所以现在的问题转换成了求\(R_i, L_i\), 就拿\(R_i\)来举例把, 反正都是一样的.

\[R_i = max(f_{j-1}+fab(i-j+1)-(sum_i-sum_{j-1})+g_{j+1}) \]

​ 发现这个式子惊奇的和上面那个长的差不多, 没错, 也可以用斜率优化!

​ 拆开后的式子是这样的 :

\[2ij + 2R_i - (2g_{i+1}-2sum_{i+1}+i^2+3i +2) = 2f_{j-1}+2sum_{j-1}+j^2 - 3j \]

​ 然后把\((j, 2f_{j-1}+2sum_{j-1}+j^2 - 3j)\)看成点就好了.

​ 注意这题有个细节, 我调了好久, 就是计算\(f_i, g_i\)的时候, 如果说算出来的是负数的话, 他可以和0区\(max\), 但是计算\(R_i\)的时候就不可以与0区\(max\)了, 因为\(R_i\)有一段区间是必选的.

#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 = 3e5 + 5;
const long long inf = 1e18;
int n;
int a[N];
long long f[N], g[N], C[N], R[N], sum[N];

struct point {
    long long x, y;
    point() {}
    point(long long X, long long Y) { x = X; y = Y; }
} sta[N];

point operator - (const point &a, const point &b) { return point(a.x - b.x, a.y - b.y); }

long long Cro(point a, point b) { return a.x * b.y - a.y * b.x; }

double xl(point a, point b) {
    return 1.0 * (b.y - a.y) / (b.x - a.x);
}

int top;

void insert(int x, long long y) {
    point p = point(x, y);
    while(top >= 2 && Cro(sta[top] - sta[top - 1], p - sta[top - 1]) >= 0) top --;
    sta[++ top] = p;
}

long long calc(point j, long long i) {
    return j.y - i * j.x;
}

long long find(long long k, int f) {
    if(!top) return 0;
    int l = 2, r = top - 1, mid, ans = 1;
    while(l <= r) {
        mid = (l + r) >> 1;
        double z1 = xl(sta[mid - 1], sta[mid]), z2 = xl(sta[mid], sta[mid + 1]);
        if(k <= z1 && k >= z2) { ans = mid; break ; }
        else if(k < z1 && k < z2) l = mid + 1; 
        else r = mid - 1;
    }
    long long res;
    if(f) res = -inf; else res = 0; // *****
    return max(max(calc(sta[1], k), calc(sta[top], k)), max(res, calc(sta[ans], k)));
}

void calc(long long *f) {
    for(int i = 1;i <= n; i++) sum[i] = sum[i - 1] + a[i]; top = 0;
    for(int i = 1;i <= n; i++) {
        long long res = find(2ll * i, 0);
        f[i] = max(0ll, res - 2ll * sum[i] + 1ll * i * i + i) / 2;
        f[i] = max(f[i - 1], f[i]); 
        insert(i, 2ll * f[i] + 2ll * sum[i] + 1ll * i * i - i);
    }
}

void solve(int l, int r) {
    if(l == r) { C[l] = max(C[l], 1ll - a[l]); return ; }
    int mid = (l + r) >> 1;
    top = 0;
    for(int i = l;i <= mid; i++) insert(i, 2ll * f[i - 1] + 2ll * sum[i - 1] + 1ll * i * i - 3ll * i);
    for(int i = mid + 1;i <= r; i++) R[i] = (find(2ll * i, 1) - 2ll * sum[i] + 2ll * g[i + 1] + 1ll * i * i + 3ll * i + 2) / 2;
    long long Max = -inf;
    for(int i = r;i > mid; i--) {
        Max = max(Max, R[i]); 
        C[i] = max(C[i], Max);
    }   
    top = 0;
    for(int i = mid + 1;i <= r; i++) insert(i, 2ll * g[i + 1] - 2ll * sum[i] + 1ll * i * i + 3ll * i);
    for(int i = l;i <= mid; i++) R[i] = (find(2ll * i, 1) + 2ll * sum[i - 1] + 2ll * f[i - 1] + 1ll * i * i - 3ll * i + 2) / 2;
    Max = -inf;
    for(int i = l;i <= mid; i++) {
        Max = max(Max, R[i]);
        C[i] = max(C[i], Max);
    }
    solve(l, mid); solve(mid + 1, r);
}

void Work() {
    calc(f); reverse(a + 1, a + n + 1);
    calc(g); reverse(a + 1, a + n + 1); reverse(g + 1, g + n + 1);
    // for(int i = 1;i <= n; i++) cout << f[i] << " "; cout << "\n";
    // for(int i = 1;i <= n; i++) cout << g[i] << " "; cout << "\n";
    for(int i = 1;i <= n; i++) sum[i] = sum[i - 1] + a[i];
    for(int i = 1;i <= n; i++) C[i] = -inf;
    solve(1, n);
    // for(int i = 1;i <= n; i++) cout << C[i] << " "; cout << "\n";
    int m = read();
    for(int i = 1, p, x;i <= m; i++) {
        p = read(); x = read();
        printf("%lld\n", max(f[p - 1] + g[p + 1], C[p] + a[p] - x));
    }
}

int main() {

    freopen("score.in","r",stdin); freopen("score.out","w",stdout);

    n = read(); 
    for(int i = 1;i <= n; i++) a[i] = read();
    Work();

    fclose(stdin); fclose(stdout);

    return 0;
}

T2

​ 有\(n\)个人, 每个人有\(a_i\)个物品. 每个人刚开始拿的都是同一种物品.

​ 有\(m\)次操作, 给定两个参数\(x,y\), 表示\(x,y\)这两个人可以拿自己的任意一种物品和对方交换, 当然也可以不交换. 问第一个人在\(m\)次操作结束后手上最多有多少不同的物品.

\(n,m \leq 3000\).

​ 最大流.

​ 首先我们可以先转化一下问题 :

​ 我们要求第一个人最多的种数而不是个数, 所以我们不妨设第一个人每种物品只拿了一个, 这样显然不会使答案变得更劣.

​ 那么我们就可以认为, 每个人手上只有1个物品, 但是手里最多可以拿\(a_i\)个物品.

​ 我们考虑这么建图, 从原点向每个人连一条流量为1的边, 然后把每个人分裂成\(m\)个点, 第\(m\)次操作就往\(x,y\)中间连一条双向边, 流量都是1, 第\(i\)个人分裂出的第\(j\)个点要向第\(j+1\)个点连一条流量为\(a_i\)的边, 最后第1个人的第\(m\)个分裂点向汇点连一条流量为\(a_1\)的边, 然后跑最大流就可以了.

#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 = 1e6, inf = 1e9, M = 1e7;
int n, m, s, t, tot, cnt;
int a[N], d[N], las[N], cur[N], head[N];
struct edge { int c, to, nxt; } e[M];

void Clear() {
    cnt = 1; tot = 0;
    memset(head, 0, sizeof(head));
    memset(las, 0, sizeof(las));
}

void add(int x, int y, int z) {
    // cout << x << " " << y << " " << z << "++++\n";
    e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].c = z;
    e[++ cnt].nxt = head[y]; head[y] = cnt; e[cnt].to = x; e[cnt].c = 0;
}

void Init() {
    n = read(); m = read();
    for(int i = 1;i <= n; i++) a[i] = read();
    for(int i = 1, x, y;i <= m; i++) {
        x = read(); y = read();
        int t1 = ++ tot, t2 = ++ tot;
        add(t1, t2, 1); add(t2, t1, 1);
        if(las[x]) add(las[x], t1, a[x]); else add(las[x], t1, 1);
        if(las[y]) add(las[y], t2, a[y]); else add(las[y], t2, 1);
        las[x] = t1; las[y] = t2;
    }
    t = ++ tot; add(las[1], t, a[1]);
}

int bfs() {
    for(int i = s;i <= t; i++) d[i] = inf, cur[i] = head[i];
    queue <int> q; q.push(s); d[s] = 0;
    while(q.size()) {
        int x = q.front(); q.pop();
        for(int i = head[x]; i ; i = e[i].nxt) {
            int y = e[i].to;
            // cout << x << " " << y << "!!!\n";
            if(e[i].c && d[y] == inf) {
                d[y] = d[x] + 1; q.push(y);
                if(y == t) return 1;
            }
        }
    }
    return 0;
}

int dfs(int x, int want) {
    if(x == t || !want) return want;
    int f, get = 0;
    for(int i = cur[x]; i && want; i = e[i].nxt) {
        int y = e[i].to;
        if(!e[i].c || d[y] != d[x] + 1) continue ;
        f = dfs(y, min(want, e[i].c));
        if(!f) d[y] = -1; 
        else want -= f, get += f, e[i].c -= f, e[i ^ 1].c += f;
        cur[x] = i;
    }
    return get;
}

void Work() {
    int res = 0;
    while(bfs()) { res += dfs(s, inf); }
    printf("%d\n", res);
}

int main() {

    freopen("collection.in","r",stdin); freopen("collection.out","w",stdout);

    for(int T = read(); T ; T --) {
        Clear(); Init(); Work();
    }

    fclose(stdin); fclose(stdout);

    return 0;
}

/*
2
3 3
3 2 1
2 3
1 2
1 2
3 3
3 1 2
2 3
1 2
1 2
*/

T3

​ 咕咕咕咕咕咕咕........

posted @ 2021-03-12 22:50  C锥  阅读(51)  评论(0编辑  收藏  举报