CSP2022

  • Post author:
  • Post category:其他




T1 假期计划



题目大意

规定



d

i

s

(

x

,

y

)

dis(x, y)






d


i


s


(


x


,




y


)





表示一张图上点



x

x






x









y

y






y





的最短距离。

给定一张无向图(图上所有边的边权都是



1

1






1





,并且给定图上每一个点一个点权记为



v

a

l

u

e

x

value_x






v


a


l


u



e










x





















),让你选定



4

4






4





个图上的点



a

,

b

,

c

,

d

a, b, c, d






a


,




b


,




c


,




d





,使得



d

i

s

(

1

,

a

)

,

d

i

s

(

a

,

b

)

,

d

i

s

(

b

,

c

)

,

d

i

s

(

c

,

d

)

dis(1, a), dis(a, b), dis(b, c), dis(c, d)






d


i


s


(


1


,




a


)


,




d


i


s


(


a


,




b


)


,




d


i


s


(


b


,




c


)


,




d


i


s


(


c


,




d


)









d

i

s

(

d

,

1

)

dis(d, 1)






d


i


s


(


d


,




1


)





都小于等于



k

+

1

k + 1






k




+








1





。并且要求



v

a

l

u

e

a

+

v

a

l

u

e

b

+

v

a

l

u

e

c

+

v

a

l

u

e

d

value_a + value_b + value_c + value_d






v


a


l


u



e










a




















+








v


a


l


u



e










b




















+








v


a


l


u



e










c




















+








v


a


l


u



e










d





















最小。



分析

首先发现点数比较小,甚至可以



O

(

n

2

)

O(n^2)






O


(



n










2









)





,所以我们可以直接对于每一个点



x

x






x





暴力



b

f

s

(

x

)

bfs(x)






b


f


s


(


x


)





求出



x

x






x





到其他节点的最短路径长度。

然后如果考虑暴力,那么就可以直接枚举



4

4






4





个点然后判断能否符合条件(就是



d

i

s

(

x

,

y

)

dis(x, y)






d


i


s


(


x


,




y


)





是否小于等于



k

+

1

k + 1






k




+








1





),就可以暴力统计答案了,但是这样复杂度



O

(

n

4

)

O(n^4)






O


(



n










4









)





显然爆炸,所以我们要考虑优化一下这个做法。

我们考虑只枚举



b

,

c

b, c






b


,




c





这两个点,因为我们发现



a

a






a





点对于



b

b






b





点来说和



d

d






d





点对于



c

c






c





点来说约束条件是一样的,具体来说就是:

  1. 对于



    a

    ,

    b

    a, b






    a


    ,




    b





    来说,



    a

    a






    a





    的约束条件是



    d

    i

    s

    (

    1

    ,

    a

    )

    k

    +

    1

    d

    i

    s

    (

    a

    ,

    b

    )

    k

    +

    1

    dis(1, a) \le k + 1 \land dis(a, b) \le k + 1






    d


    i


    s


    (


    1


    ,




    a


    )













    k




    +








    1













    d


    i


    s


    (


    a


    ,




    b


    )













    k




    +








    1




  2. 对于



    d

    ,

    c

    d, c






    d


    ,




    c





    来说,



    d

    d






    d





    的约束条件是



    d

    i

    s

    (

    1

    ,

    d

    )

    k

    +

    1

    d

    i

    s

    (

    d

    ,

    c

    )

    k

    +

    1

    dis(1, d) \le k + 1 \land dis(d, c) \le k + 1






    d


    i


    s


    (


    1


    ,




    d


    )













    k




    +








    1













    d


    i


    s


    (


    d


    ,




    c


    )













    k




    +








    1




这俩玩意儿是不是长得一模一样,所以我们考虑对于每一个点



x

x






x





预处理出另一个点



y

y






y





使得



y

y






y





满足



d

i

s

(

1

,

y

)

k

+

1

d

i

s

(

x

,

y

)

k

+

1

dis(1, y) \le k + 1 \land dis(x, y) \le k + 1






d


i


s


(


1


,




y


)













k




+








1













d


i


s


(


x


,




y


)













k




+








1





,且



v

a

l

u

e

y

value_y






v


a


l


u



e










y





















取到最大值,这里记录这样的



y

y






y









f

x

f_x







f










x





















。这个预处理显然是



O

(

n

2

)

O(n^2)






O


(



n










2









)





的,然后再枚举点



b

b






b









c

c






c





,然后直接然后直接用



v

a

l

u

e

b

+

v

a

l

u

e

c

+

v

a

l

u

e

f

b

+

v

a

l

u

e

f

c

value_b + value_c + value_{f_b} + value_{f_c}






v


a


l


u



e










b




















+








v


a


l


u



e










c




















+








v


a


l


u



e












f










b





































+








v


a


l


u



e












f










c






































作为本次的答案,每次取



max

\max






max





就好了。

但是这样做会有一个问题,可能发现我们求到的



f

b

f_{b}







f











b


























f

c

f_c







f










c





















是同一个点,这样就与题目意思不符了,所以我们还需要高出次大的点,记为



s

b

s_b







s










b

























s

c

s_c







s










c





















但是这样还是有一个问题,我们发现你有可能



f

b

=

c

f_b = c







f










b




















=








c









f

c

=

b

f_c = b







f










c




















=








b





并且



s

b

=

s

c

s_b = s_c







s










b




















=









s










c





















这样就又不行了,所以我们需要再弄出第三大值



t

b

,

t

c

t_b, t_c







t










b


















,





t










c





















,这样就一定是正确的了,具体的操作可以看代码。



代码

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 3030
#define MAXM 100100 
#define int long long
const int INFI = 1e18;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9')
		x = x * 10 + c - '0', c = getchar();
	return x;
}

int n = 0, m = 0, k = 0;
int tot = 0;
int first[MAXN] = { 0 };
int   nxt[MAXM] = { 0 };
int    to[MAXM] = { 0 };
int value[MAXN] = { 0 };
inline void add(int x, int y){
	nxt[++tot] = first[x];
	first[x] = tot, to[tot] = y;
}

int dis[MAXN][MAXN] = { 0 };
queue<int> q;
void bfs(int st){
	while(!q.empty()) q.pop();
	for(int i = 1; i <= n; i++) dis[st][i] = INFI;
	q.push(st); dis[st][st] = 0;
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int e = first[x]; e; e = nxt[e]){
			int y = to[e];
			if(dis[st][y] == INFI) dis[st][y] = dis[st][x] + 1, q.push(y);
		}
	}
}

int f[MAXN] = { 0 };                                 // 最大值 
int s[MAXN] = { 0 };                                 // 次大值
int t[MAXN] = { 0 };                                 // 第三大值 
void prework(){                                      // 对于每一个 c 选出最大的 d
	for(int c = 2; c <= n; c++)
		for(int d = 2; d <= n; d++){
			if(c == d) continue;
			if(dis[1][d] <= k and dis[d][c] <= k){
				if(value[d] > value[f[c]]) t[c] = s[c], s[c] = f[c], f[c] = d;
				else{
					if(value[d] > value[s[c]]) t[c] = s[c], s[c] = d;
					else if(value[d] > value[t[c]]) t[c] = d;
				}
			}
		}
}

inline bool can(int a, int b, int c, int d) { return a != d and a != c and d != b and a and d; }
void solve(){
	int ans = 0;
	for(int b = 2; b <= n; b++)
		for(int c = b + 1, a, d; c <= n; c++){
			if(dis[b][c] > k) continue;
			/* 这里懒得分类讨论,所以直接每都看可不可行然后取 max 作为答案 */
			a = f[b], d = f[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
			a = f[b], d = s[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
			a = f[b], d = t[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
			a = s[b], d = f[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
		    a = s[b], d = s[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
			a = s[b], d = t[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
			a = t[b], d = f[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
			a = t[b], d = s[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
			a = t[b], d = t[c];
			if(can(a, b, c, d)) ans = max(ans, value[a] + value[b] + value[c] + value[d]);
		}
	cout << ans << '\n';
}

signed main(){
//	freopen("a.in", "r", stdin);
	n = in, m = in, k = in; k++;
	for(int i = 2; i <= n; i++) value[i] = in;
	for(int i = 1; i <= m; i++){
		int x = in, y = in; 
		add(x, y), add(y, x);
	} 
	for(int i = 1; i <= n; i++) bfs(i);
	prework(); solve();
	return 0;
} 



T2



题目大意

给定两个数组



a

a






a









b

b






b





,长度分别为



n

,

m

n, m






n


,




m





,现在有



q

q






q





个询问,每次询问给出



l

1

,

r

1

,

l

2

,

r

2

l_1, r_1, l_2, r_2







l










1


















,





r










1


















,





l










2


















,





r










2





















,每次询问假定有两个人在进行博弈,甲需要在



a

[

l

1

r

1

]

a[l_1 \sim r_1]






a


[



l










1






























r










1


















]





中选出一个数字



x

x






x





,乙需要在



b

[

l

2

r

2

]

b[l_2\sim r_2]






b


[



l










2






























r










2


















]





中选出一个数字



y

y






y





,最终得分就是



x

y

xy






x


y





,甲的目的是让最终得分尽量大,乙则希望他尽量小,并且甲乙两个人都足够聪明,每次询问输出最后应该得出的答案。



分析

粗略想一下应该是区间最值(线段树就好了),但是又有正负值之分,如果甲选了区间最大,并且最大是正值,乙选了区间最小,并且最小是负值,这样显然对甲不利,所以甲应该要选一个负数?再想想应该也不太对,因为如果甲选择负数的话乙就可以选一个最大的正数让答案最小。

思考到这里发现好像分正负值分类讨论有一点困难,所以我们考虑分



16

16






1


6





种情况把所有的可能性都列举出来,具体来说也就是:





  1. a

    a






    a





    中非负数的最小值






    b

    b






    b





    中非负数的最小值

    的乘积





  2. a

    a






    a





    中非负数的最小值






    b

    b






    b





    中非负数的最大值

    的乘积





  3. a

    a






    a





    中非负数的最小值






    b

    b






    b





    中非正数的最小值

    的乘积





  4. a

    a






    a





    中非负数的最小值






    b

    b






    b





    中非正数的最大值

    的乘积





  5. a

    a






    a





    中非负数的最大值






    b

    b






    b





    中非负数的最小值

    的乘积





  6. a

    a






    a





    中非负数的最大值






    b

    b






    b





    中非负数的最大值

    的乘积





  7. a

    a






    a





    中非负数的最大值






    b

    b






    b





    中非正数的最小值

    的乘积





  8. a

    a






    a





    中非负数的最大值






    b

    b






    b





    中非正数的最大值

    的乘积





  9. a

    a






    a





    中非正数的最小值






    b

    b






    b





    中非负数的最小值

    的乘积





  10. a

    a






    a





    中非正数的最小值






    b

    b






    b





    中非负数的最大值

    的乘积





  11. a

    a






    a





    中非正数的最小值






    b

    b






    b





    中非正数的最小值

    的乘积





  12. a

    a






    a





    中非正数的最小值






    b

    b






    b





    中非正数的最大值

    的乘积





  13. a

    a






    a





    中非正数的最大值






    b

    b






    b





    中非负数的最小值

    的乘积





  14. a

    a






    a





    中非正数的最大值






    b

    b






    b





    中非负数的最大值

    的乘积





  15. a

    a






    a





    中非正数的最大值






    b

    b






    b





    中非正数的最小值

    的乘积





  16. a

    a






    a





    中非正数的最大值






    b

    b






    b





    中非正数的最大值

    的乘积

这样就可以把所有的情况都考虑进去了。然后只需要在最后选出满足条件的答案就好了。

现在问题又来了,怎么选出满足条件的答案呢?现在我们把这些情况先列出来一下看看(下面的



a

p

m

i

n

apmin






a


p


m


i


n





就表示



a

a






a





中非负数的最小值,



b

n

m

a

x

bnmax






b


n


m


a


x





就表示



b

b






b





中非正数的最大值):

apmin =



x

1

x_1







x










1




















apmax =



x

2

x_2







x










2




















anmin =



x

3

x_3







x










3




















anmax =



x

4

x_4







x










4




















bpmin =



y

1

y_1







y










1























x

1

y

1

x_1y_1







x










1



















y










1























x

2

y

1

x_2y_1







x










2



















y










1























x

3

y

1

x_3y_1







x










3



















y










1























x

4

y

1

x_4y_1







x










4



















y










1




















bpmax =



y

2

y_2







y










2























x

1

y

2

x_1y_2







x










1



















y










2























x

2

y

2

x_2y_2







x










2



















y










2























x

3

y

2

x_3y_2







x










3



















y










2























x

4

y

2

x_4y_2







x










4



















y










2




















bnmin =



y

3

y_3







y










3























x

1

y

3

x_1y_3







x










1



















y










3























x

2

y

3

x_2y_3







x










2



















y










3























x

3

y

3

x_3y_3







x










3



















y










3























x

4

y

3

x_4y_3







x










4



















y










3




















bnmax =



y

4

y_4







y










4























x

1

y

4

x_1y_4







x










1



















y










4























x

2

y

4

x_2y_4







x










2



















y










4























x

3

y

4

x_3y_4







x










3



















y










4























x

4

y

4

x_4y_4







x










4



















y










4




















我们先观察一列,发现这里的



x

x






x





都是一样的,所以我们只需要取出这一列中



x

y

i

xy_i






x



y










i





















的最小值作为这一列的答案,再把这四列的答案取最大值得到最后的答案即可。



代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
#define in read()
#define MAXN 100100
const int INFI = 1e18;

inline int read() {
	int x = 0, f = 0; char c = getchar();
	while(c < '0' or c > '9') {
		if(c == '-') f = 1; c = getchar();}
	while('0' <= c and c <= '9')
		x = x * 10 + c - '0', c = getchar();
	return f ? -x : x;
}

int a[MAXN] = { 0 };
int b[MAXN] = { 0 };
int n = 0, m = 0, q = 0;

struct SegmentTreemin{
	struct Tnode { int l, r, dat; };
	int arr[MAXN];
	Tnode t[MAXN << 2];
	inline void up(int p) { t[p].dat = min(t[ls(p)].dat, t[rs(p)].dat); }
	void build(int p, int l, int r){
		t[p].l = l, t[p].r = r;
		if(l == r) { t[p].dat = arr[l]; return; }
		int mid = l + r >> 1;
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
		up(p);
	}
	int query(int p, int l, int r){
		if(l <= t[p].l and t[p].r <= r) return t[p].dat;
		int mid = t[p].l + t[p].r >> 1, ans = INFI;
		if(l <= mid) ans = min(ans, query(ls(p), l, r));
		if(r >  mid) ans = min(ans, query(rs(p), l, r));
		return ans;
	}
}Tapmin, Tanmin, Tbpmin, Tbnmin;

struct SegmentTreemax{
	struct Tnode { int l, r, dat; };
	int arr[MAXN];
	Tnode t[MAXN << 2];
	inline void up(int p) { t[p].dat = max(t[ls(p)].dat, t[rs(p)].dat); }
	void build(int p, int l, int r){
		t[p].l = l, t[p].r = r;
		if(l == r) { t[p].dat = arr[l]; return; }
		int mid = l + r >> 1;
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
		up(p);
	}
	int query(int p, int l, int r){
		if(l <= t[p].l and t[p].r <= r) return t[p].dat;
		int mid = t[p].l + t[p].r >> 1, ans = -INFI;
		if(l <= mid) ans = max(ans, query(ls(p), l, r));
		if(r >  mid) ans = max(ans, query(rs(p), l, r));
		return ans;
	}
}Tapmax, Tanmax, Tbpmax, Tbnmax;

void init(){
	for(int i = 1; i <= max(n, m); i++) 
		Tapmin.arr[i] = Tanmin.arr[i] = Tbpmin.arr[i] = Tbnmin.arr[i] = INFI;
	for(int i = 1; i <= max(n, m); i++) 
		Tapmax.arr[i] = Tanmax.arr[i] = Tbpmax.arr[i] = Tbnmax.arr[i] = -INFI;
	for(int i = 1; i <= n; i++){
		if(a[i] >= 0) Tapmin.arr[i] = Tapmax.arr[i] = a[i];             // 非负数
		if(a[i] <= 0) Tanmin.arr[i] = Tanmax.arr[i] = a[i];             // 非正数
	}
	for(int i = 1; i <= m; i++){
		if(b[i] >= 0) Tbpmin.arr[i] = Tbpmax.arr[i] = b[i];             // 非负数
		if(b[i] <= 0) Tbnmin.arr[i] = Tbnmax.arr[i] = b[i];             // 非正数
	}
	Tapmin.build(1, 1, n), Tapmax.build(1, 1, n), Tanmin.build(1, 1, n), Tanmax.build(1, 1, n);
	Tbpmin.build(1, 1, m), Tbpmax.build(1, 1, m), Tbnmin.build(1, 1, m), Tbnmax.build(1, 1, m);
}

int solve(int x, int l2, int r2){
	int y = INFI;
	int t1 = Tbpmin.query(1, l2, r2);                                   // b+min
	int t2 = Tbpmax.query(1, l2, r2);                                   // b+max
	int t3 = Tbnmin.query(1, l2, r2);                                   // b-min
	int t4 = Tbnmax.query(1, l2, r2);                                   // b-max
	if(abs(t1) != INFI) y = min(y, x * t1); 
	if(abs(t2) != INFI) y = min(y, x * t2);
	if(abs(t3) != INFI) y = min(y, x * t3);
	if(abs(t4) != INFI) y = min(y, x * t4);
	return y;
}

int solve(int l1, int r1, int l2, int r2){
	int x = 0, y = INFI, ans = -INFI;
	x = Tapmin.query(1, l1, r1);
	if(abs(x) != INFI) ans = max(ans, solve(x, l2, r2));
	x = Tapmax.query(1, l1, r1);
	if(abs(x) != INFI) ans = max(ans, solve(x, l2, r2));
	x = Tanmin.query(1, l1, r1);
	if(abs(x) != INFI) ans = max(ans, solve(x, l2, r2));
	x = Tanmax.query(1, l1, r1);
	if(abs(x) != INFI) ans = max(ans, solve(x, l2, r2));
	return ans;
}

signed main(){
	n = in, m = in, q = in;
	for(int i = 1; i <= n; i++) a[i] = in;
	for(int i = 1; i <= m; i++) b[i] = in;
	init();
	 while(q--){
	 	int l1 = in, r1 = in, l2 = in, r2 = in;
	 	cout << solve(l1, r1, l2, r2) << '\n';
	 }
	return 0;
} 



T3



T4



版权声明:本文为ID246783原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。