【2021杭电多校赛】2021“MINIEYE杯”中国大学生算法设计超级联赛(9)签到题4题

网友投稿 622 2022-05-29

Solved Pro.ID Title Ratio(Accepted / Submitted)

1001 NJU emulator 23.27%(37/159)

1002 Just another board game 24.38%(569/2334)(博弈)

1003 Dota2 Pro Circuit 28.72%(664/2312)(双指针,模拟

1004 Into the woods 40.00%(2/5)

1005 Did I miss the lethal? 21.69%(36/166)

1006 Guess the weight 25.00%(120/480)

1007 Boring data structure problem 17.04%(445/2611)(双端队列,模拟)

1008 Integers Have Friends 2.0 10.16%(129/1270)

1009 Little prince and the garden of roses 6.12%(3/49)

1010 Unfair contest 12.40%(254/2049)(贪心,模拟,分类讨论)

1011 ZYB’s kingdom 17.50%(7/40)

题意:

给出一个n*m的棋盘,每个位置上有一个数字,默认棋子在(1,1),n*m<1e5。

A和B轮流操作,A可以将棋子移动到同一行的任何位置(也可以不移动),B可以将棋子移动到同一列中的任何位置。

第k轮游戏结束,或者A和B也可以在每一轮选择立刻结束游戏。

A想最大化结束时的值,B想最小化,求两人都是最优策略下,最后的数字。

思路:

如果不允许随时结束,考虑A的策略为,B下一轮肯定会给它找一个列最小,所以我要找跳到所有列中列最小值最大的位置,B同理会选择所有行中最大值最小的行,这样下一轮的A就不能获得更大的值。这样,对于k是偶数次的情况,最后一次是B操作,那么A的最佳方案就是列最小的最大,如果k是奇数,那么就是行最大的最小。

考虑随时可以结束,因为都是最佳方案,所以最后的值已经定下了。那么如果最开始的位置比自己后手的方案更优的话,我就可以在一开始就结束掉以获得对自己更有利的值,加一个特判就行。

#include typedef long long LL; using namespace std; int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int T; cin>>T; while(T--){ LL n, m, k; cin>>n>>m>>k; vector >vc; for(int i = 1; i <= n; i++){ vectortmp; for(int j = 1; j <= m; j++){ int x; cin>>x; tmp.push_back(x); } vc.push_back(tmp); } if(k==1){ //A在第1行最大化 int ans = 0; for(int x : vc[0])ans = max(ans, x); cout<

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

题意:

n只队伍参加两轮比赛,给出第一轮结束后每个队的分数,以及第2论第i名能获得的分数。

求第i只队伍的最好名次和最差名次(允许同分名次并列)

思路:

对于最高排名,先给它最大的得分,再给剩下的n-1个队,去找能否让其得分<=sc,把所有的都找出来,答案就是补集。

对于最低排名,先给它最小的得分,在去剩下的n-1个队里找,如果得分直接比它大,那没救了直接更新,如果得分比sc小,就去分数里从大到小找,知道第一个加起来都比sc小的就用它即可。

#include typedef long long LL; using namespace std; const LL maxn = 5050; LL a[maxn], b[maxn]; LL r[maxn]; void init(LL n){for(LL i = 1; i <= n; i++)r[i]=i;} bool cmp(LL x, LL y){ return a[x]>a[y]; } bool cmp2(LL x, LL y){ return x>y; } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); LL T; cin>>T; while(T--){ LL n; cin>>n; for(LL i = 1; i <= n; i++)cin>>a[i]; for(LL i = 1; i <= n; i++)cin>>b[i]; init(n); sort(r+1,r+n+1,cmp); sort(b+1,b+n+1,cmp2); for(LL i = 1; i <= n; i++){ LL hi = 0, mi = 0; //最高排名 LL sc = a[i]+b[1], k = 2; //给它最大的分 for(LL j = n; j >= 1; j--){//得分从小到大枚举剩下的 LL jj = r[j]; if(jj==i)continue; while(a[jj]+b[k]>sc && k<=n)k++;//找得分<=sc的 if(k<=n)hi++; //找得到就++,让hi尽可能多 k++; if(k>n)break; } hi = n-hi;//剩下的分都比它高 //最低排名 sc = a[i]+b[n]; k = n-1; for(LL j = 1; j <= n; j++){ LL jj = r[j]; if(jj==i)continue; if(a[jj]>sc){mi++; continue;} else{ while(k>=1 && a[jj]+b[k]<=sc)k--; if(k<=0)break; mi++; k--; } } cout<

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

【2021杭电多校赛】2021“MINIEYE杯”中国大学生算法设计超级联赛(9)签到题4题

42

43

44

45

46

47

48

49

50

51

52

53

54

题意:

维护一个队列,初始为空。1e7次操作,每次可以在左端或右端插入一个数(保证所有数不重复),或者从队列中删除一个指定的数值,或者询问ceil(m+1)/2的值。

思路:

1e7相当于所有操作都要满足O(1),对于查询中间位置,容易想到开两个双端队列维护,因为每次只插入一个数,不难维护前者和后者长度一样,中间位置就是答案。

对于删除操作,因为插入的值是1-1e7的,可以用数组记录每个队中的数属于哪个队列,删除的时候直接从vis里改成0并更新对应队列的长度即可,并不用真的去队列里找在什么地方去删,查询的时候提前把vis==0的值去掉就行。注意删除后也要维护长度一样,不然G后面直接Q查询的话就会WA。

#include typedef long long LL; using namespace std; const int maxn = 1e7+10; int vis[maxn], tot; dequelq, rq; int lc, rc; void del(){ //每次修改长度后,维护lc==rc或lc+1==rc while(lc>rc){ while(vis[lq.back()]==0)lq.pop_back(); rq.push_front(lq.back()); lq.pop_back(); rc++; lc--; vis[rq.front()]=2; } while(rc-1>lc){ while(vis[rq.front()]==0)rq.pop_front(); lq.push_back(rq.front()); rq.pop_front(); lc++; rc--; vis[lq.back()]=1; } } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int T; cin>>T; while(T--){ string op; cin>>op; if(op[0]=='L'){ lq.push_front(++tot); vis[tot]=1; lc++; del(); }else if(op[0]=='R'){ rq.push_back(++tot); vis[tot]=2; rc++; del(); }else if(op[0]=='G'){ int x; cin>>x; if(vis[x]==1)lc--; if(vis[x]==2)rc--; vis[x] = 0; del(); }else if(op[0]=='Q'){ while(vis[rq.front()]==0)rq.pop_front(); cout<

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

题意:

两个人比赛,n个裁判给分,给分范围为[1-h],去掉s个最高分和t个最低分,剩下的为得分。

给出n-1个裁判的给分,第n个裁判想让第1个人赢得比赛,并且最小化给1的分数a[n]-给2的分数b[n]。

思路:

首先不难想到,对a和b数组从小到大排序,然后取区间[t+1, n-1-s]的和,即去掉最大值最小值的原始分sc1和sc2。

对于增加的第n个值,有三种情况,要么比a[t]还小,那么不计入分数,会把a[t]挤出来计入分数。要么比a[n-s]还大,把a[n-s]挤出来替换分数,要么就在这之间保持原状,此时新增的值的取值范围必须在[ a[t], a[n-s] ]之间。然后分类讨论即可。

#include typedef long long LL; using namespace std; const int maxn = 1e5+10; LL a[maxn], b[maxn]; int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int T; cin>>T; while(T--){ int n, s, t, h; cin>>n>>s>>t>>h; for(int i = 1; i < n; i++)cin>>a[i]; for(int i = 1; i < n; i++)cin>>b[i]; sort(a+1,a+n); sort(b+1,b+n); a[0] = b[0] = 1; a[n] = b[n] = h; int l = t+1, r = n-1-s; LL sc1 = 0, sc2 = 0; for(int i = l; i <= r; i++){ sc1 += a[i]; sc2+=b[i]; } //给1尽可能小,2尽可能大 LL ami = sc1+a[l-1], amx = sc1+a[r+1]; LL bmi = sc2+b[l-1], bmx = sc2+b[r+1]; if(amx <= bmi)cout<<"IMPOSSIBLE\n"; //1打最大,2打最小都不行,没救了 else if(ami > bmx)cout<<1-h<<"\n"; //1打最小,2打最大,那最好了 else{ LL ans = 0; if(ami > bmi)ans = max(ans, a[l-1]-1);//给1打1分,新的分可以比2大,那就打 if(bmx < amx)ans = max(ans, h-b[r+1]);//给2打满分,新的分可以比1小,那就打 cout<

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

视频接入服务 VIS

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:面试官:说说什么是Java内存模型?
下一篇:技术分享 | 做为测试,那些必须掌握的测试技术体系
相关文章