Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3)
题目大意:给定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\n",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 }
题目大意:给出一个排列每一个位置的前缀最大值,还原该排列,答案可能有多种,还原任意一种即可。
输入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\n"); 49 else{ 50 for(int i=1;i<=n;++i) printf("%d%c",ans[i],i==n?'\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 }
题目大意:给定一个括号串,我们可以翻转某一区间的括号,即对[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,'\n'); 59 for(int i=1;i<=m;++i) printf("%d %d\n",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\n",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,'\n'); 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,就稍微改了下映射关系。