ABC370 A-F题解 (AtCoder Beginner Contest 370)
感觉ABC中间1-2道题,经常考数据结构,vector、set、map这些。
A
这类简单题,看清楚这个位置,作为检查,可以有效减低出错可能性:
Output
Print Yes
, No
, or Invalid
according to the instructions in the problem statement.
B
无
C
std,这样写(0->n-1,n-1->0),可以少写一个vector
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() { 5 string s, t; 6 cin >> s >> t; 7 vector<string> ans; 8 vector<int> v; 9 int n = s.size(); 10 for (int i = 0; i < n; i++) { 11 if (s[i] > t[i]) v.push_back(i); 12 } 13 for (int i = n - 1; i >= 0; i--) { 14 if (s[i] < t[i]) v.push_back(i); 15 } 16 int sz = v.size(); 17 for (int i = 0; i < sz; i++) { 18 s[v[i]] = t[v[i]]; 19 ans.push_back(s); 20 } 21 cout << sz << '\n'; 22 for (string e : ans) cout << e << '\n'; 23 }
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define ULL unsigned long long 5 6 const LL mod_1=1e9+7; 7 const LL mod_2=998244353; 8 9 const double eps_1=1e-5; 10 const double eps_2=1e-10; 11 12 const int maxn=2e5+10; 13 14 vector<int> v1,v2; 15 16 int main() 17 { 18 int i,cnt=0; 19 string s1,s2; 20 cin>>s1>>s2; 21 for (i=0;i<s1.size();i++) 22 if (s1[i]>s2[i]) 23 v1.emplace_back(i), cnt++; 24 else if (s1[i]<s2[i]) 25 v2.emplace_back(i), cnt++; 26 cout<<cnt<<endl; 27 for (auto d:v1) 28 { 29 s1[d] = s2[d]; 30 cout<<s1<<endl; 31 } 32 reverse(v2.begin(),v2.end()); 33 for (auto d:v2) 34 { 35 s1[d] = s2[d]; 36 cout<<s1<<endl; 37 } 38 39 return 0; 40 }
D
我比赛的时候,就感觉找一个数的pre、next和存在修改,感觉很眼熟。感觉有很多题是这样的思路。
首先,它有删除操作。然后删除后的序列,和寻找删除的位置,很容易让人联想到需要排序(lower_bound),才能找相邻的位置。
但是,vector删除挺耗时的。然后想到set。
另外要注意的是,lower_bound(srow[i].begin(), srow[i].end(), j); 超时,srow[i].lower_bound(j); AC了
题解的set操作,就写得很精简。up、down操作都用"auto it = g2[C].lower_bound(R);",首先up,删除一个数,它不影响down中"auto it = g2[C].lower_bound(R)"的结果。有的删除两个数,如果分别删除 prev(it,1)、it,应该会因为删除一个数后,it的位置发生变动,然后删除第二个数这个操作是错误的。
it位置前/后几个数:prev(it) next(it) prev(it,g) next(it,g)
set的lower_bound:set1.lower_bound(i)
vector< set<int> > srow(row+1), scol(col+1)
set1.count(i): set::count()是C++ STL中的内置函数,它返回元素在集合中出现的次数。由于set容器仅包含唯一元素,因此只能返回1或0
1 #include <cassert> 2 #include <iostream> 3 #include <set> 4 #include <vector> 5 using namespace std; 6 7 int main() { 8 cin.tie(nullptr); 9 ios::sync_with_stdio(false); 10 11 int H, W, Q; 12 cin >> H >> W >> Q; 13 14 vector<set<int>> g1(H), g2(W); 15 for (int i = 0; i < H; i++) { 16 for (int j = 0; j < W; j++) { 17 g1[i].insert(j); 18 g2[j].insert(i); 19 } 20 } 21 22 auto erase = [&](int i, int j) { g1[i].erase(j), g2[j].erase(i); }; 23 24 while (Q--) { 25 int R, C; 26 cin >> R >> C; 27 --R, --C; 28 29 if (g1[R].count(C)) { 30 erase(R, C); 31 continue; 32 } 33 34 // up 35 { 36 auto it = g2[C].lower_bound(R); 37 if (it != begin(g2[C])) erase(*prev(it), C); 38 } 39 // down 40 { 41 auto it = g2[C].lower_bound(R); 42 if (it != end(g2[C])) erase(*it, C); 43 } 44 // left 45 { 46 auto it = g1[R].lower_bound(C); 47 if (it != begin(g1[R])) erase(R, *prev(it)); 48 } 49 // right 50 { 51 auto it = g1[R].lower_bound(C); 52 if (it != end(g1[R])) erase(R, *it); 53 } 54 } 55 56 int ans = 0; 57 for (int i = 0; i < H; i++) ans += g1[i].size(); 58 cout << ans << "\n"; 59 }
1 /* 2 TODO 想别人是怎么写pre、next和修改后的处理的。这类题,遇到多次。1 怎么简洁 2 多种方法 3 4 5 6 up down都,auto it = g2[C].lower_bound(R);,先up,然后删除一个数,不影响down中,auto it = g2[C].lower_bound(R)的结果 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 #define LL long long 11 #define ULL unsigned long long 12 13 const LL mod_1=1e9+7; 14 const LL mod_2=998244353; 15 16 const double eps_1=1e-5; 17 const double eps_2=1e-10; 18 19 const int maxn=2e5+10; 20 21 int main() 22 { 23 int row,col,q,i,j,value1,value2,cnt=0; 24 cin>>row>>col>>q; 25 26 set<int> *srow,*scol; 27 srow = new set<int>[row+1]; 28 scol = new set<int>[col+1]; 29 for (i=1;i<=row;i++) 30 for (j=1;j<=col;j++) 31 srow[i].insert(j); 32 for (j=1;j<=col;j++) 33 for (i=1;i<=row;i++) 34 scol[j].insert(i); 35 36 set<int>::iterator k; 37 38 while (q--) 39 { 40 /* 41 for (i=1;i<=row;i++) 42 { 43 for (auto d:srow[i]) 44 cout<<d<<" "; 45 cout<<endl; 46 } 47 cout<<endl; 48 */ 49 50 cin>>i>>j; 51 if (!srow[i].empty()) 52 { 53 54 //begin()有数字,是第一个数;end()没有数字 55 //prev(it,g) next(it,g) 56 //srow[i].lower_bound(j); ac ; lower_bound(srow[i].begin(), srow[i].end(), j);过不了 57 58 //k = lower_bound(srow[i].begin(), srow[i].end(), j); 59 k = srow[i].lower_bound(j); 60 if (k!=srow[i].end() && (*k)==j) 61 { 62 srow[i].erase(j); 63 scol[j].erase(i); 64 continue; 65 } 66 } 67 68 if (!srow[i].empty()) 69 { 70 /* 71 for (auto d:srow[i]) 72 cout<<d<<" "; 73 cout<<endl; 74 */ 75 76 //k = lower_bound(srow[i].begin(), srow[i].end(), j); 77 k = srow[i].lower_bound(j); 78 value1 = value2 = -1; 79 if (k!=srow[i].begin()) 80 value1 = * prev(k,1); 81 if (k!=srow[i].end()) 82 value2 = * k; 83 if (value1!=-1) 84 { 85 srow[i].erase(value1); 86 scol[value1].erase(i); 87 } 88 if (value2!=-1) 89 { 90 srow[i].erase(value2); 91 scol[value2].erase(i); 92 } 93 } 94 95 if (!scol[j].empty()) 96 { 97 //k = lower_bound(scol[j].begin(), scol[j].end(), i); 98 k = scol[j].lower_bound(i); 99 value1 = value2 = -1; 100 if (k!=scol[j].begin()) 101 value1 = * prev(k,1); 102 if (k!=scol[j].end()) 103 value2 = * k; 104 if (value1!=-1) 105 { 106 scol[j].erase(value1); 107 srow[value1].erase(j); 108 } 109 if (value2!=-1) 110 { 111 scol[j].erase(value2); 112 srow[value2].erase(j); 113 } 114 } 115 } 116 117 for (i=1;i<=row;i++) 118 cnt+=srow[i].size(); 119 cout<<cnt<<endl; 120 121 return 0; 122 }
我按照std,自己照葫芦画瓢写一下:
1 /* 2 TODO 想别人是怎么写pre、next和修改后的处理的。这类题,遇到多次。1 怎么简洁 2 多种方法 3 4 5 6 up down都,auto it = g2[C].lower_bound(R);,先up,然后删除一个数,不影响down中,auto it = g2[C].lower_bound(R)的结果 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 #define LL long long 11 #define ULL unsigned long long 12 13 const LL mod_1=1e9+7; 14 const LL mod_2=998244353; 15 16 const double eps_1=1e-5; 17 const double eps_2=1e-10; 18 19 const int maxn=2e5+10; 20 21 int main() 22 { 23 int row,col,q,i,j,v,cnt=0; 24 cin>>row>>col>>q; 25 26 vector< set<int> > srow(row+1),scol(col+1); 27 28 for (i=1;i<=row;i++) 29 for (j=1;j<=col;j++) 30 srow[i].insert(j); 31 for (j=1;j<=col;j++) 32 for (i=1;i<=row;i++) 33 scol[j].insert(i); 34 35 set<int>::iterator k; 36 37 while (q--) 38 { 39 cin>>i>>j; 40 if (srow[i].count(j)) 41 { 42 srow[i].erase(j); 43 scol[j].erase(i); 44 continue; 45 } 46 47 k = srow[i].lower_bound(j); 48 if (k!=srow[i].begin()) 49 { 50 v = * prev(k,1); 51 srow[i].erase(v); 52 scol[v].erase(i); 53 } 54 55 k = srow[i].lower_bound(j); 56 if (k!=srow[i].end()) 57 { 58 v = * k; 59 srow[i].erase(v); 60 scol[v].erase(i); 61 } 62 63 k = scol[j].lower_bound(i); 64 if (k!=scol[j].begin()) 65 { 66 v = * prev(k,1); 67 scol[j].erase(v); 68 srow[v].erase(j); 69 } 70 71 k = scol[j].lower_bound(i); 72 if (k!=scol[j].end()) 73 { 74 v = * k; 75 scol[j].erase(v); 76 srow[v].erase(j); 77 } 78 79 } 80 81 for (i=1;i<=row;i++) 82 cnt+=srow[i].size(); 83 cout<<cnt<<endl; 84 85 return 0; 86 }
E
s(i,j) 表示位置i到位置j的数字的和。
对于位置i,满足条件数目为f[i],f[i]=sum{f[j]} 。j<i,s(j+1,i)!=k(数的和为k不被允许)。
以位置i结尾,子串j+1~i的选择有i种情况。需要记录f[0]~f[i-1]的情况。
如果暴力做,遍历所有j的位置,O(n^2)。
用map进行统计,统计s(1,j)数字和为u时的满足条件数目。
因为数字和范围比较大,所以用map。
对于这些j,只要它们数字和s(1,j)一样,都等于u,那么都可以归为一类,因为都满足j<i的条件,没有j的大小区别。它们可以用map进行合并(数值相加)。
对于位置i,s(1,i)=ans,只要s(1,j)!=ans-k,那么即可满足s(j+1,i)!=k。
用一个变量保存当前所有满足条件数目的和。统计f[i]的时候,剔除s(1,j)=ans-k对应的满足条件数目即可。
map,如果一个数值v不存在,那么mp[v]=0,可以直接用mp[v]获得数值,不用额外写if mp.find(v)!=mp.end()。
好短代码,很快能写完。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define ULL unsigned long long 5 6 const LL mod=998244353; 7 8 const double eps_1=1e-5; 9 const double eps_2=1e-10; 10 11 const int maxn=2e5+10; 12 13 map<LL, LL> mp; 14 15 int main() 16 { 17 LL n,k,a,i,ans=0,sum=0, exc_ans, exc_sum; 18 mp[0]=1; 19 sum=1; 20 cin>>n>>k; 21 for (i=1;i<=n;i++) 22 { 23 cin>>a; 24 ans+=a; 25 26 exc_ans = ans-k; 27 exc_sum = (sum - mp[exc_ans] + mod) % mod; 28 29 (mp[ans] += exc_sum) %= mod; 30 (sum += exc_sum) %= mod; 31 32 if (i==n) 33 cout << exc_sum % mod; 34 } 35 36 return 0; 37 }
F
其实这道题,二分答案很容易想出来,关键是有n个起始位置,next(i,K)想一会也能想出来。关键是,100分钟,时间不太够了。D这类数据结构,set题,平时要多熟练一点。其实A-E能切题得很快,F其实也是emmm……
1 /* 2 关键是一个圆弧形的问题。如果要把所有开始位置都遍历一次,那么需要n次 3 n个数多展开一次,2n 4 5 多少个位置从来没有被切一刀,就是多少位置不能从它这开始 6 7 二分答案 8 记录前缀和,用lower_bound寻找下一个位置,中间段的和第一次大于等于w的位置 9 10 next(x)=y。next(x,k)等于多少[next()执行k次] next(k) k 二进制 next(1)+next(2)+next(4)+... 11 12 lower_bound,对于每个点都做:n*log(n) 13 next,对于每个点都做:n*log(k) 14 15 二分答案:log(A) 16 17 log(n) = log(k) = 17 18 A=2e5*1e4/2=1e9 19 log(A) = 30 20 21 n*log(n or k)*log(A) 22 23 17*2e5*30=1e8 24 25 一个位置能不能从来不被用:从这里开头,不行 26 27 从i开头(i=1~n)都处理的优势:可以统一处理 28 */ 29 #include <bits/stdc++.h> 30 using namespace std; 31 #define LL long long 32 #define ULL unsigned long long 33 34 const LL mod_1=1e9+7; 35 const LL mod_2=998244353; 36 37 const double eps_1=1e-5; 38 const double eps_2=1e-10; 39 40 const int maxn=4e5+10; 41 const int siz_max = 20; //30 42 43 LL a[maxn],presum[maxn], nex[maxn][siz_max]; 44 map<LL,LL> mp; 45 46 int main() 47 { 48 LL n,K,i,j,l,r,mid,siz,u,vis; 49 cin>>n>>K; 50 presum[0]=0; 51 for (i=1;i<=n;i++) 52 { 53 cin>>a[i]; 54 presum[i]=presum[i-1]+a[i]; 55 } 56 57 for (i=n+1;i<=n*2;i++) 58 { 59 a[i]=a[i-n]; 60 presum[i]=presum[i-1]+a[i]; 61 } 62 63 l=1,r=presum[n]/K; 64 //l=1,r=1e9; 65 for (siz=0;siz<siz_max;siz++) 66 { 67 nex[n*2+1][siz]=n*2+1; 68 nex[n*2+2][siz]=n*2+1; 69 } 70 71 while (l<=r) 72 { 73 mid=(l+r)/2; 74 for (i=1;i<=n*2;i++) 75 nex[i][0] = lower_bound(presum+1, presum+n*2+1, mid+presum[i-1]) - (presum+1) + 1 + 1; 76 77 for (j=1,siz=2;siz<=K;j++,siz*=2) 78 for (i=1;i<=n*2;i++) 79 nex[i][j] = nex[ nex[i][j-1] ][j-1]; 80 vis=0; 81 for (i=1;i<=n;i++) 82 { 83 j = K; 84 siz = 0; 85 u=i; 86 while (j) 87 { 88 if (j&1) 89 u = nex[u][siz]; 90 siz++; 91 j>>=1; 92 } 93 if (u<=i+n) 94 { 95 vis++; 96 //break; 97 } 98 } 99 mp[mid]=vis; 100 101 if (vis) 102 l=mid+1; 103 else 104 r=mid-1; 105 } 106 cout<<r<<" "<<n-mp[r]; 107 108 109 return 0; 110 }
G