AtCoder Beginner Contest 452(ABC452)
A - Gothec
判断一下是否是那个日子就行了。
点击查看代码
//因为月亮是最高级别的褒奖。
#include<bits/stdc++.h>
using namespace std;
int a,b;
signed main(){
cin >> a >> b;
if(a == 1&&b == 7)
cout << "Yes";
else if(a == 3&&b == 3)
cout << "Yes";
else if(a == 5&&b == 5)
cout << "Yes";
else if(a == 7&&b == 7)
cout << "Yes";
else if(a == 9&&b == 9)
cout << "Yes";
else cout << "No";
return 0;
}
B - Draw Frame
模拟一下输出。
点击查看代码
//因为月亮是最高级别的褒奖。
#include<bits/stdc++.h>
using namespace std;
int a,b;
signed main(){
cin >> a >> b;
for(int i = 1;i <= b;++ i)
cout << '#';
cout << '\n';
for(int i = 2;i < a;++ i){
cout << '#';
for(int j = 2;j < b;++ j)
cout << '.';
cout << '#';
cout << '\n';
}
for(int i = 1;i <= b;++ i)
cout << '#';
return 0;
}
C - Fishbones
首先,如果一个字符串长度不是\(n\),一定不能作为中间的字符串,那么剩下的字符串长度一定为\(n\)。
考虑定义一个数组\(g_{i,j,c}\)代表长度为\(i\),第\(j\)个字符为\(c\)的字符串有几个,这样当字符串\(s_i\)作为中间的字符串时,我们就可以枚举\(0 \leq j < n\),看看有没有长度为\(a_j\),且第\(b_j\)个字符是\(s_{i,j}\)的字符串,如果对于所有的\(j\)都有,那么\(s_i\)就可以作为中间字符串。
那么\(g\)数组怎么求呢?对于一个字符串\(s\),考虑它能给\(g\)的哪些位置做出贡献,设\(len\)为\(s\)的长度,那么它能对\(g_{len,j,s_j}(0 \leq j < len)\)这些位置做出\(1\)的贡献。
点击查看代码
//因为月亮是最高级别的褒奖。
#include<bits/stdc++.h>
using namespace std;
int n;
int a[15],b[15];
int m;
string s[200005];
int g[15][15][30];
//g[i][j][k]代表了长度为i,第j个字符为k的字符串个数
signed main(){
cin >> n;
for(int i = 0;i < n;++ i)
cin >> a[i] >> b[i],b[i]--;
cin >> m;
for(int i = 1;i <= m;++ i){
cin >> s[i];
int len = s[i].length();
for(int j = 0;j < len;++ j)
g[len][j][s[i][j]-'a']++;
}
for(int i = 1;i <= m;++ i){
if(s[i].length() != n){
cout << "No\n";
continue;
}
bool fl = 1;
for(int j = 0;j < n;++ j){
if(!g[a[j]][b[j]][s[i][j]-'a']){
fl = 0;break;
}
}
if(fl) cout << "Yes\n";
else cout << "No\n";
}
return 0;
}
D - No-Subsequence Substring
考虑对每一个\(l\)统计有多少个\(r\)符合要求。
那么什么样的\(r\)是符合要求的呢?\([l,r]\)中不包含\(T\)的就是符合要求的。那么我们找到一个\(pos\)使得\([l,pos]\)包含\(T\)且\(\forall j,l \leq j < pos\)都有\([l,j]\)不包含\(T\),那么\(l \leq r < pos\)的\(r\)就是符合要求的了。
\(pos\)怎么找呢?从\(l\)开始找一个子序列,可以定义一个指针放在\(l\)的位置,然后枚举这个子序列(也就是\(T\))的每一个字符,每次找到指针后第一个是这个字符的位置,把指针放到那个位置,一直找到最后一个字符就行了。要注意每次要从指针的后一个位置开始找,否则如果有几个连着的一样的字符,就会一直找到这个位置,就会出错。还有就是如果找不到某个字符了,那就是\(l\)后不包含\(T\)这个子序列,那么\(r\)就可以取到\(l\)一直到最后。
那么怎样找到某一个位置后的第一个某一个字符呢?做一个预处理即可。(这个应该很简单,看代码吧)
点击查看代码
//因为月亮是最高级别的褒奖。
#include<bits/stdc++.h>
using namespace std;
string s,t;
int sn,tn;
int nxt[200005][30];
signed main(){
cin >> s >> t;
sn = s.length(),tn = t.length();
for(int i = 0;i < 26;++ i) nxt[sn][i] = sn;
for(int i = sn-1;i >= 0;-- i){
for(int j = 0;j < 26;++ j)
nxt[i][j] = nxt[i+1][j];
nxt[i][s[i]-'a'] = i;
}
int ans = 0;
for(int l = 0;l < sn;++ l){
int pos = l-1;
for(int i = 0;i < tn;++ i){
pos = nxt[pos+1][t[i]-'a'];
if(pos >= sn){
pos = -1;
break;
}
}
if(pos == -1) ans += sn-1-l+1;
else ans += pos-1-l+1;
}
cout << ans;
return 0;
}
E - You WILL Like Sigma Problem
先推一下式子。
\(\begin{aligned}
\sum_{i=1}^{N} \sum_{j=1}^{M}A_iB_j(i mod j) &= \sum_{i=1}^{N} \sum_{j=1}^{M}A_iB_j(i-\lfloor \frac{i}{j} \rfloor j)\\
&=\sum_{i=1}^{N} \sum_{j=1}^{M}A_iB_ji-\sum_{i=1}^{N} \sum_{j=1}^{M}A_iB_j\lfloor \frac{i}{j} \rfloor j\\
&=\sum_{i=1}^{N} A_ii\sum_{j=1}^{M}B_j-\sum_{j=1}^{M}B_jj\sum_{i=1}^{N} A_i\lfloor \frac{i}{j} \rfloor
\end{aligned}\)
前半部分可以记录一下\(b\)数组的和,枚举\(i\)来\(O(n)\)算,那么后半部分肯定是要枚举\(j\)的,但是怎么算呢?可以发现,\(\forall k,0 \leq k \times j \leq n\),都有\(max(0,k \times j) \leq i < min(n,(k+1) \times j)\)的\(i\),\(\lfloor \frac{i}{j} \rfloor = k\),那么我们可以分区间来算\(\sum_{i=1}^{N} A_i\lfloor \frac{i}{j} \rfloor\),把它分成\([1,1 \times j-1],[1 \times j,2 \times j-1],[2 \times j,3 \times j-1]……\)这些区间来算,枚举每一个区间,那么\(\lfloor \frac{i}{j} \rfloor\)就是固定值了,可以提出来,我们要的就是这个区间的\(a_i\)的和,做一个前缀和即可。那么枚举区间的复杂度是\(O(\sum_{j=1}^{m} \frac{n}{j})\)的,也就是\(O(nlogn)\)的,可以过。
点击查看代码
//因为月亮是最高级别的褒奖。
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int n,m;
int a[500005];
int q[500005];
int b[500005];
int sumb;
signed main(){
cin >> n >> m;
for(int i = 1;i <= n;++ i)
cin >> a[i],q[i] = q[i-1]+a[i],q[i] %= mod;
for(int i = 1;i <= m;++ i)
cin >> b[i],sumb += b[i],sumb %= mod;
int ans = 0;
for(int i = 1;i <= n;++ i)
ans += a[i]*i%mod*sumb%mod,ans %= mod;
for(int j = 1;j <= m;++ j){
int res = 0;
for(int k = 0;k*j <= n;++ k){
res += (q[min(n,(k+1)*j-1)]+mod-q[max(1ll,k*j)-1])%mod*k%mod;
res %= mod;
}
res *= b[j]*j%mod;res %= mod;
ans += mod-res;ans %= mod;
}
cout << ans;
return 0;
}
F - Interval Inversion Count
首先我们考虑把等于\(k\)的个数转化为小于等于\(k\)的个数减去小于等于\(k-1\)的个数。
那么逆序对个数小于等于\(k\)的区间个数怎么求呢?在这个题中,因为要用双指针,所以区间左右端点用\(x\)和\(y\),考虑固定\(y\)看有多少个\(x\)符合要求。
考虑双指针,首先把\(l\)固定在\(1\),找到最大的\(r\)使得\([l,r]\)的逆序对个数小于等于\(k\)。在这个过程中的\(y = r\),所有\(1 \leq x \leq r\)都是满足条件的\(x\),要加入答案。然后,每次让\(r\)向后移一个,再让\(l\)后移直到\([l,r]\)的逆序对个数小于等于\(k\)(当然也可能不移动)。那么对于\(y = r\),\(l \leq x \leq r\)都是符合要求的,要加入答案。重复这个过程知道\(r\)达到\(n\)。
观察发现本题逆序对个数可以用树状数组维护。(具体怎么维护的看代码吧)
点击查看代码
//因为月亮是最高级别的褒奖。
#include<bits/stdc++.h>
#define lb(x) x&(-x)
#define int long long
using namespace std;
int n,k;
int a[500005];
int c[500005];
void add(int x,int v){
for(;x <= n;x += lb(x))
c[x] += v;
}
int sum(int x){
int res = 0;
for(;x;x -= lb(x)) res += c[x];
return res;
}
int calc(int k){
memset(c,0,sizeof(c));
int l = 1,r = 0;
int res = 0,ans = 0;
for(r = 1;r <= n;++ r){
add(a[r],1);
res += sum(n)-sum(a[r]);
if(res > k){
res -= sum(n)-sum(a[r]);
add(a[r],-1);
r--;
break;
}
ans += r-l+1;
}
while(l <= r&&r < n){
r++;
add(a[r],1);
res += sum(n)-sum(a[r]);
while(res > k&&l < r){
add(a[l],-1);
res -= sum(a[l]-1);
l++;
}
ans += r-l+1;
}
return ans;
}
signed main(){
cin >> n >> k;
for(int i = 1;i <= n;++ i)
cin >> a[i];
cout << calc(k)-calc(k-1);
return 0;
}

浙公网安备 33010602011771号