• 【前后缀 + 推公式整理】 Codeforces Round #813 (Div. 2) D. Empty Graph


    题意:

    给定 n n n 个点的点权 a i a_i ai,这 n n n 个点两两之间连边构成一个无向完全图,
    e d g e ( l , r ) edge(l,r) edge(l,r) 的边权为 m i n ( a l , a l + 1 , . . . , a r ) , ( 1 ≤ l < r ≤ n ) min(a_l, a_{l+1},...,a_r),(1\leq lmin(al,al+1,...,ar),(1l<rn)
    d ( u , v ) d(u,v) d(u,v) 是点 u u u 和点 v v v 的最短路径,图上直径为最大的 d ( u , v ) d(u,v) d(u,v)
    现在需要最大化直径,有 k k k 次修改点权的机会,问最大化的直径为多少。

    数据范围:
    1 ≤ k ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 9 1\leq k\leq n\leq 10^5, 1\leq a_i\leq 10^9 1kn105,1ai109

    题解:

    二分答案为 x x x

    对于任意两个点的 d ( u , v ) d(u,v) d(u,v),不妨设 u < v uu<v,方便起见: e d g e ( u , v ) edge(u,v) edge(u,v) 表示 u u u v v v 边的权值
    u u u v v v 的最短路径可以从两部分考虑:

    • 走一次,权值为 e d g e ( u , v ) edge(u,v) edge(u,v)
    • 走两次,中间点设为 m i d mid mid ,权值为 e d g e ( u , m i d ) + e d g e ( m i d , v ) edge(u,mid)+edge(mid,v) edge(u,mid)+edge(mid,v),这里的 m i d mid mid 必然不在 u u u v v v 构成的区间中,否则不如执行第一次操作。
      • m i d < u midmid<u,那么权值为 e d g e ( m i d , u ) + e d g e ( m i d , v ) edge(mid,u)+edge(mid,v) edge(mid,u)+edge(mid,v)
        等价于找到最小的 a m i d a_{mid} amid m i d < u midmid<u
      • m i d > v mid>v mid>v,那么权值为 e d g e ( u , m i d ) + e d g e ( v , m i d ) edge(u,mid)+edge(v,mid) edge(u,mid)+edge(v,mid)
        等价于找到最小的 a m i d a_{mid} amid m i d > v mid>v mid>v

    枚举 u u u v v v,对于 a u , a u + 1 , . . . a v a_u,a_{u+1},...a_v au,au+1,...av,小于 x x x 的部分都需要一次操作,
    对于剩余的点权 a m i d a_{mid} amid a m i d × 2 < x a_{mid}\times 2 amid×2<x 的部分都需要一次操作。

    但是枚举两个点的复杂度是 O ( n 2 ) O(n^2) O(n2) 的,考虑如何继续优化

    p r e pre pre 为前缀中 a i × 2 < x a_i\times 2 < x ai×2<x 的数量
    s u f suf suf 为后缀中 a i × 2 < x a_i\times 2 < x ai×2<x 的数量
    p r e 2 pre2 pre2 为前缀中 a i < x a_iai<x 的数量
    枚举 u u u v v v ,使得 d ( u , v ) ≥ x d(u,v)\geq x d(u,v)x 的操作次数为:
    ( p r e [ u − 1 ] ) + ( s u f [ v + 1 ] ) + ( p r e 2 [ v ] − p r e 2 [ u − 1 ] ) (pre[u-1])+(suf[v+1])+(pre2[v]-pre2[u-1]) (pre[u1])+(suf[v+1])+(pre2[v]pre2[u1])
    等价于
    ( p r e [ u − 1 ] − p r e 2 [ u − 1 ] ) + ( s u f [ v + 1 ] + p r e 2 [ v ] ) (pre[u-1]-pre2[u-1])+(suf[v+1]+pre2[v]) (pre[u1]pre2[u1])+(suf[v+1]+pre2[v])

    记录 m i n u minu minu 为前缀中 p r e [ u − 1 ] − p r e 2 [ u − 1 ] pre[u-1]-pre2[u-1] pre[u1]pre2[u1] 的最小值
    记录 m i n v minv minv 为后缀中 s u f [ v + 1 ] + p r e 2 [ v ] suf[v+1]+pre2[v] suf[v+1]+pre2[v] 的最小值
    然后枚举 i i i 为分割点,判断是否存在一个 u u u,使得 m i n u [ i ] + m i n v [ i + 1 ] ≤ k minu[i]+minv[i+1]\leq k minu[i]+minv[i+1]k 即可
    这里甚至可以记录 m i n u [ i ] minu[i] minu[i] m i n v [ i + 1 ] minv[i+1] minv[i+1] 的最小值所在索引,从而确定对应的区间 [ u , v ] [u,v] [u,v]

    代码:

    #include 
    using namespace std;
    
    typedef long long ll;
    
    const int N = 100010;
    const int MAX = 1000000000;
    int a[N], n, k;
    
    int pre2[N];
    int pre[N];
    int suf[N];
    int minj[N];
    int mini[N];
    bool check(int x) {
    	
    	pre[0] = 0;
    	for (int i = 1; i <= n; ++i) {
    		pre[i] = pre[i - 1];
    		if (a[i] * 2 < x) pre[i] += 1;
    		pre2[i] = pre2[i - 1];
    		if (a[i] < x) pre2[i] += 1; 
    	} 
    	
    	suf[n + 1] = 0; 
    	for (int i = n; i >= 1; --i) {
    		suf[i] = suf[i + 1];
    		if (a[i] * 2 < x) suf[i] += 1;
    	}
    	
    	// 找到一对(i, j)
    	//	pre2[j] - pre2[i - 1] + suf[j + 1] + pre[i - 1];
    	//	(pre2[j] + suf[j + 1]) + (pre[i - 1] - pre2[i - 1]);
    	
    	mini[0] = MAX;
    	for (int i = 1; i <= n; ++i) {
    		mini[i] = min(mini[i - 1], pre[i - 1] - pre2[i - 1]);
    	}
    	
    	minj[n + 1] =  MAX;
    	for (int j = n; j >= 1; --j) {
    		minj[j] = min(minj[j + 1], pre2[j] + suf[j + 1]);
    	}
    	
    	for (int i = 1; i + 1 <= n; ++i) {
    		if (mini[i] + minj[i + 1] <= k) return true;
    	}
    	return false;
    }
    
    void solve() {
    	scanf("%d%d", &n, &k);
    	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	
    	int l = 1, r = MAX;
    	while (l < r) {
    		int mid = l + r + 1 >> 1;
    		if (check(mid)) l = mid;
    		else r = mid - 1;
    	}
    	printf("%d\n", l);
    }
    
    int main()
    {
    	int T = 1;
    	scanf("%d", &T);
    	for (int i = 1; i <= T; ++i) {
    		solve();
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
  • 相关阅读:
    手机在网状态-手机在网状态查询-手机在网站状态接口
    uniapp的h5端在线预览文件
    分治类dp:1017T3
    Linux操作系统~匿名管道和命名管道的使用及其原理分析
    css选择器(通配符、(元素、类、id)、后代(子代、所有后代)、兄弟(相邻兄弟、所有兄弟)、属性(属性、指定值的属性)、交集并集)
    【Rust指南】基础语法|基本数据类型|复合数据类型
    MySQL中多表查询、表连接(内连接和外连接)
    微信小程序配置
    使用pg_hint_plan定义Postgresql执行计划
    arthas 源码构建
  • 原文地址:https://blog.csdn.net/weixin_43900869/article/details/126329740