Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3)

A.Math Problem(CF 1262 A)

题目大意:给定n条线段,求一条线段,使得这个线段能够跟所有给定的线段都相交(端点值一样也算相交),最小化它的长度,可以是0.

很显然找出这n条线段的左端点最大值和右端点的最小值,它们的差和0的最大值即为答案。

 1 #include <bits/stdc++.h>
 2 #define MIN(a,b) (((a)<(b)?(a):(b)))
 3 #define MAX(a,b) (((a)>(b)?(a):(b)))
 4 using namespace std;
 5 
 6 template <typename T>
 7 void read(T &x) {
 8     int s = 0, c = getchar();
 9     x = 0;
10     while (isspace(c)) c = getchar();
11     if (c == 45) s = 1, c = getchar();
12     while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
13     if (s) x = -x;
14 }
15 
16 template <typename T>
17 void write(T x, char c = ' ') {
18     int b[40], l = 0;
19     if (x < 0) putchar(45), x = -x;
20     while (x > 0) b[l++] = x % 10, x /= 10;
21     if (!l) putchar(48);
22     while (l) putchar(b[--l] | 48);
23     putchar(c);
24 }
25 
26 void Input(void) {
27     int n,l,r;
28     l=0;
29     r=1e9+7;
30     read(n);
31     for(int u,v,i=1;i<=n;++i){
32         read(u);
33         read(v);
34         l=MAX(l,u);
35         r=MIN(v,r);
36     }
37     printf("%d
",MAX(l-r,0));
38 }
39 
40 void Solve(void) {}
41 
42 void Output(void) {}
43 
44 main(void) {
45     int kase;
46     freopen("input.txt", "r", stdin);
47     freopen("output.txt", "w", stdout);
48     read(kase);
49     for (int i = 1; i <= kase; i++) {
50         //printf("Case #%d: ", i);
51         Input();
52         Solve();
53         Output();
54     }
55 }
神奇的代码

B.Box(CF 1262 B)

题目大意:给出一个排列每一个位置的前缀最大值,还原该排列,答案可能有多种,还原任意一种即可。

输入max[i],如果max[i-1]<max[i],则第i位一定是max[i].

如果max[i-1]=max[i],则我们可以选择小于max[i]的未出现的数作为第i位,贪心构造即可。当没有数可以选择的时候则说明没有排列满足该条件。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 template <typename T>
 5 void read(T &x) {
 6     int s = 0, c = getchar();
 7     x = 0;
 8     while (isspace(c)) c = getchar();
 9     if (c == 45) s = 1, c = getchar();
10     while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
11     if (s) x = -x;
12 }
13 
14 template <typename T>
15 void write(T x, char c = ' ') {
16     int b[40], l = 0;
17     if (x < 0) putchar(45), x = -x;
18     while (x > 0) b[l++] = x % 10, x /= 10;
19     if (!l) putchar(48);
20     while (l) putchar(b[--l] | 48);
21     putchar(c);
22 }
23 int n,l,ans[100050];
24 bool used[100050];
25 void Input(void) {
26     read(n);
27     for(int i=1;i<=n;++i) used[i]=true;
28     l=1;
29     ans[0]=0;
30     bool qwq=false;
31     for(int u,v=0,i=1;i<=n;++i){
32         read(u);
33         if (qwq) continue;
34         if (u>v){
35             used[u]=false;
36             ans[i]=u;
37             v=u;
38             continue;
39         }
40         while(l<u&&used[l]==false) ++l;
41         if (l==u){
42             qwq=true;
43             continue;
44         }
45         ans[i]=l;
46         used[l]=false;
47     }
48     if (qwq) printf("-1
");
49     else{
50         for(int i=1;i<=n;++i) printf("%d%c",ans[i],i==n?'
':' ');
51     }
52 }
53 
54 void Solve(void) {}
55 
56 void Output(void) {}
57 
58 main(void) {
59     int kase;
60     freopen("input.txt", "r", stdin);
61     freopen("output.txt", "w", stdout);
62     read(kase);
63     for (int i = 1; i <= kase; i++) {
64         //printf("Case #%d: ", i);
65         Input();
66         Solve();
67         Output();
68     }
69 }
神奇的代码

C.Messy (CF 1262 C)

题目大意:给定一个括号串,我们可以翻转某一区间的括号,即对[l...r]翻转,则变成[r...l],然后我们需要进行不超过n次操作,使得该括号串合法,且对于所有该串的前缀子串,有k个合法子串,输出操作次数以及前后分别进行的操作的区间的左端点和右端点,注意只要小于n即可,不需要最小。题目保证有解。

所有子串有k个合法前缀子串,即整个子串分成了k份,我们对某一份中的()交换,则将该份子串拆成两份,我们把两份子串的接触点)(交换,则这两个子串合并成一个,所以我们关键是如何让该子串合法,然后就不会了qwq

坑待填填坑啦!!!

注意到n不大,我们可以构造一个符合条件的括号序列ans,然后将初始序列que按照构造的序列进行翻转,即考虑第i个位置,若ans[i]==que[i]则continue,若不相等则找一个最小的大于i的j,有ans[i]==que[j],然后对que的区间[i,j]进行翻转,复杂度O(n2)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 template <typename T>
 5 void read(T &x) {
 6     int s = 0, c = getchar();
 7     x = 0;
 8     while (isspace(c)) c = getchar();
 9     if (c == 45) s = 1, c = getchar();
10     while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
11     if (s) x = -x;
12 }
13 
14 template <typename T>
15 void write(T x, char c = ' ') {
16     int b[40], l = 0;
17     if (x < 0) putchar(45), x = -x;
18     while (x > 0) b[l++] = x % 10, x /= 10;
19     if (!l) putchar(48);
20     while (l) putchar(b[--l] | 48);
21     putchar(c);
22 }
23 int n,k;
24 
25 int op[2006][2];
26 
27 char ans[2005],que[2005];
28 
29 void change(int st,int en){
30     char qwq[2005];
31     int l=0;
32     for(int i=st;i<=en;++i) qwq[l++]=que[i];
33     for(int i=st;i<=en;++i) que[i]=qwq[--l];
34 }
35 void Input(void) {
36     read(n);
37     read(k);
38     int m=0;
39     scanf("%s",que);
40     for(int i=1;i<=(n-2*(k-1))/2;++i)
41         ans[m++]='(';
42     for(int i=1;i<=(n-2*(k-1))/2;++i)
43         ans[m++]=')';
44     for(int i=1;i<k;++i){
45         ans[m++]='(';
46         ans[m++]=')';
47     }
48     m=0;
49     for(int i=0;i<n;++i){
50         if (ans[i]!=que[i]){
51             int j=i+1;
52             while(ans[i]!=que[j]) ++j;
53             change(i,j);
54             op[++m][0]=i+1;
55             op[m][1]=j+1;
56         }
57     }
58     write(m,'
');
59     for(int i=1;i<=m;++i) printf("%d %d
",op[i][0],op[i][1]);
60 }
61 
62 void Solve(void) {}
63 
64 void Output(void) {}
65 
66 main(void) {
67     int kase;
68     freopen("input.txt", "r", stdin);
69     freopen("output.txt", "w", stdout);
70     read(kase);
71     for (int i = 1; i <= kase; i++) {
72         //printf("Case #%d: ", i);
73         Input();
74         Solve();
75         Output();
76     }
77 }
神奇的代码

 

D.Optimal Subsequences(CF 1262 D1、D2)

题目大意:给定一个长度为n的数组,求一个长度为k的最佳序列,该序列满足两个条件:其和是所有长度相同的序列的和的最大值;该序列是所有长度相同且和最大的序列中,其数字构成的序列的字典序最小,输出该序列的第pos位的数字,多次询问。

很容易想到我们可以先对k进行从小到大排序,依次构造出长度为k的最佳序列,其构造方法很容易想到,即设最佳序列的数组为a,数大的先添加进a数组,数相等的位置在前的先添加。

但关键在于如何找到该序列中第pos位的数字。

我们新设一个数组cnt,cnt[i]表示第i个数字被选中了,记为1,否则为0,然后我们用树状数组来维护cnt数组的前缀和,当某位的前缀和为pos,且该位被选中了,则该位的数字即为所求答案。由于前缀和单调递增,我们可以采用二分,即可在O(log2n)时间内找出。

总时间复杂度是O(mlog2n+mlogm)

 

当时做的时候逆过来,k从大到小排序,然后在原数组删数,树状数组维护前i个数中被删去的个数,则对于在原数组第i位且未被删去的数,它在最佳序列的位置即为i-sum[i],同样二分找即可。

  1 #include <bits/stdc++.h>
  2 #define lowbit(x)  ((x&(-x)))
  3 using namespace std;
  4 
  5 template <typename T>
  6 void read(T &x) {
  7     int s = 0, c = getchar();
  8     x = 0;
  9     while (isspace(c)) c = getchar();
 10     if (c == 45) s = 1, c = getchar();
 11     while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
 12     if (s) x = -x;
 13 }
 14 
 15 template <typename T>
 16 void write(T x, char c = ' ') {
 17     int b[40], l = 0;
 18     if (x < 0) putchar(45), x = -x;
 19     while (x > 0) b[l++] = x % 10, x /= 10;
 20     if (!l) putchar(48);
 21     while (l) putchar(b[--l] | 48);
 22     putchar(c);
 23 }
 24 
 25 struct data{
 26     int val,id;
 27     bool operator < (const data &b) const{
 28         if (val>b.val) return true;
 29         if (val<b.val) return false;
 30         if (id<b.id) return true;
 31         return false;
 32     }
 33 };
 34 
 35 const int N=2e5+5;
 36 
 37 struct date{
 38     int k,pos,id;
 39 }que[N];
 40 
 41 priority_queue<data> qwq;
 42 
 43 int n,m;
 44 
 45 int tree[N],a[N],ans[N];
 46 
 47 bool dell[N];
 48 
 49 bool cmp(const struct date &a,const struct date &b){
 50     if (a.k>b.k) return true;
 51     if (a.k<b.k) return false;
 52     if (a.pos<b.pos) return false;
 53     return true;
 54 }
 55 
 56 void Input(void) {
 57     read(n);
 58     for(int i=1;i<=n;++i) {
 59         read(a[i]);
 60         qwq.push(data{a[i],i});
 61     }
 62     read(m);
 63     for(int i=1;i<=m;++i){
 64         que[i].id=i;
 65         read(que[i].k);
 66         read(que[i].pos);
 67     }
 68 }
 69 
 70 void tree_insert(int x){
 71     for(int i=x;i<=n;i+=lowbit(i))
 72         tree[i]++;
 73 }
 74 
 75 int tree_sum(int x){
 76     int a=0;
 77     for(int i=x;i>=1;i-=lowbit(i))
 78         a+=tree[i];
 79     return a;
 80 }
 81 
 82 int find_pos(int x){
 83     int l=1,r=n;
 84     while(l<=r){
 85         int mid=(l+r)>>1;
 86         int tmp=tree_sum(mid);
 87         if (mid-tmp>x) r=mid;
 88         else if (mid-tmp==x) if (dell[mid]==false) return a[mid];
 89         else r=mid;
 90         else l=mid+1;
 91     }
 92     return 0;
 93 } 
 94 
 95 void Solve(void) {
 96     sort(que+1,que+1+m,cmp);
 97     int qvq=n;
 98     for(int i=1;i<=m;++i){
 99         int dis=qvq-que[i].k;
100         qvq=que[i].k;
101         while(dis--){
102             data qaq=qwq.top();
103             dell[qaq.id]=true;
104             tree_insert(qaq.id);
105             qwq.pop();
106         }
107         ans[que[i].id]=find_pos(que[i].pos);
108     }
109 }
110 
111 void Output(void) {
112     for(int i=1;i<=m;++i) printf("%d
",ans[i]);
113 }
114 
115 main(void) {
116     freopen("input.txt", "r", stdin);
117     freopen("output.txt", "w", stdout);
118     Input();
119     Solve();
120     Output();
121 }
神奇的代码

(以为会T的结果才跑了200+ms?)

E. Arson In Berland Forest (CF 1262 E)

题目大意:某森林发生火灾,一棵树如果着火,下一个时刻火焰回向四周八个方向蔓延,给定当前局面的着火情况,最大化着火时间,并给定初始时刻(T=0)时树的起火情况。如果有多种情况输出任意一种即可。

最暴力的做法自然是从着火边缘不断灭火,时刻加一,直到某一时刻,如果灭了火之后不能还原成原来的着火情况,则当前时刻为最大的着火时间,其复杂度为O(nmT),T能取到min(m,n)的数量级,显然会T。

但是我们从暴力做法中发现,局面是否可行具有单调性,于是我们可以二分时间来判断是否可行,复杂度降为O(nm*logT),T=min(n,m)

至于验证方法,(听说BFS卡常?),我们可以先根据X预处理二维前缀和,在二分某个时间t时,找到每个含有(2*t+1)*(2*t+1)个X的区域,把它缩成中心点(在判断的时候它们还是保留为X的),最后再还原判断是否为原来的局面(因为可能存在不够(2*t+1)*(2*t+1)个X的区域,使得它无法缩为点,而我们只是把所有含有(2*t+1)*(2*t+1)个X的区域缩为点而已)。判断的话我们令着火区域的值增加1,运用二维差分来维护某区域的值,即可在最后判断每个点的着火情况与原图是否一致。

 1 #include <bits/stdc++.h>
 2 #define MIN(a,b) ((((a)<(b)?(a):(b))))
 3 #define MAX(a,b) ((((a)>(b)?(a):(b))))
 4 #define ABS(a) ((((a)>0?(a):-(a))))
 5 #define POS(i,j) (((i)*(m+2)+(j)))
 6 using namespace std;
 7 
 8 template <typename T>
 9 void read(T &x) {
10     int s = 0, c = getchar();
11     x = 0;
12     while (isspace(c)) c = getchar();
13     if (c == 45) s = 1, c = getchar();
14     while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
15     if (s) x = -x;
16 }
17 
18 template <typename T>
19 void write(T x, char c = ' ') {
20     int b[40], l = 0;
21     if (x < 0) putchar(45), x = -x;
22     while (x > 0) b[l++] = x % 10, x /= 10;
23     if (!l) putchar(48);
24     while (l) putchar(b[--l] | 48);
25     putchar(c);
26 }
27 
28 const int N=3e6+8;
29 
30 int n,m,cnt;
31 
32 int sum[N],now[N],ans[N];
33 
34 char ma[N];
35 
36 
37 void Input(void) {
38     read(n);
39     read(m);
40     memset(sum,0,sizeof(sum));
41     for(int i=1;i<=n;++i) scanf("%s",ma+POS(i,1));
42     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) sum[POS(i,j)]=(ma[POS(i,j)]=='X');
43     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) sum[POS(i,j)]+=sum[POS(i-1,j)];
44     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) sum[POS(i,j)]+=sum[POS(i,j-1)];
45     cnt=0;
46 }
47 
48 bool check(int x){
49     ++cnt;
50     memset(now,0,sizeof(now));
51     for(int i=1+x;i<=n-x;++i) for(int j=1+x;j<=m-x;++j){
52         if ((sum[POS(i+x,j+x)]-sum[POS(i-x-1,j+x)]-sum[POS(i+x,j-x-1)]+sum[POS(i-x-1,j-x-1)])!=((2*x+1)*(2*x+1))) continue;
53         ans[POS(i,j)]=cnt;
54         now[POS(i-x,j-x)]++;
55         now[POS(i+x+1,j+x+1)]++;
56         now[POS(i-x,j+x+1)]--;
57         now[POS(i+x+1,j-x)]--;
58     }
59     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) now[POS(i,j)]+=now[POS(i-1,j)];
60     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) now[POS(i,j)]+=now[POS(i,j-1)];
61     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if ((ma[POS(i,j)]=='X')^(now[POS(i,j)]!=0)) return false;
62     return true;
63 }
64 void Solve(void) {
65     int T=0,l=0,r=((MIN(n,m)>>1)+1);
66     int tt=0;
67     while(l<r){
68         int mid=(l+r)>>1;
69         if (check(mid)) T=mid,l=mid+1,tt=cnt;
70         else r=mid;
71     }
72     write(T,'
');
73     for(int i=1;i<=n;++i) {
74         for(int j=1;j<=m;++j) 
75             printf("%c",ans[POS(i,j)]>=tt?'X':'.');
76         puts("");
77     }
78 }
79 
80 void Output(void) {}
81 
82 main(void) {
83     freopen("input.txt", "r", stdin);
84     freopen("output.txt", "w", stdout);
85     Input();
86     Solve();
87     Output();
88 }
神奇的代码

原图n,m都有可能到达1e6但相乘不会大于1e6,然后用了二维到一维的映射函数POS,由于正常标号可能会变成负数导致RE,就稍微改了下映射关系。

原文地址:https://www.cnblogs.com/Lanly/p/11924231.html