ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

省选测试12

2021-03-12 23:03:51  阅读:181  来源: 互联网

标签:12 sta 省选 max top mid long ch 测试


省选测试 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

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

标签:12,sta,省选,max,top,mid,long,ch,测试
来源: https://www.cnblogs.com/czhui666/p/14526872.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有