A - Candies and Two Sisters(签到)
#include <iostream>
using namespace std;
int main()
{
int t,n;
cin>>t;
while(t--){
cin>>n;
if(n<=2){
cout<<"0"<<endl;
continue;
}
if(n&1) cout<<n/2<<endl;
else cout<<n/2-1<<endl;
}
return 0;
}
B - Construct the String(构造)
1.题目大意:现在我们需要构造出一个长度为
n
n
n的字符串使得任意长度为
a
a
a的子串都只含有
b
b
b个不同的字符
2.一开始写尺取, W A WA WA了,找不到错误在哪。实际上先构造出一个长度为 a a a的字符串向后不断平移即可
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
int t,n,a,b;
string ans;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
while(t--){
cin>>n>>a>>b;
ans="";
for(int i=0;i<b;i++) {
ans+='a'+i;
}
for(int i=b+1;i<=a;i++)
ans+='a';
for(int i=a+1;i<=n;i++)
ans+=ans[i-a-1];
cout<<ans<<endl;
}
return 0;
}
//尺取大法=wa
/*#include <iostream>
#include <cstring>
using namespace std;
char s[2020];
int num[30];
int main()
{
int t,n,a,b;
cin>>t;
while(t--){
cin>>n>>a>>b;
memset(num,0,sizeof num);
int l=0,r=0,cnt=0;
while(r<n){
while(r-l<a && r<n){
if(cnt==b){
s[r]='a';
num[0]++;
}else{
for(int i=0;i<26;i++)
if(!num[i]){
s[r]=i+'a';
num[i]++;
cnt++;
break;
}
}
r++;
}
if(--num[l]==0) cnt--;
l++;
}
for(int i=0;i<n;i++)
cout<<s[i];
cout<<"\n";
}
return 0;
}*/
C - Two Teams Composing(二分)
1.题目大意:给出若干个数,从中任取,能否分成长度相同两部分使得第一部分各个数都不相同,第二部分只有一种数字。求最大分配长度
2.对于右半部分,只考虑最大数量 M A X MAX MAX的数字即可。除去最大数字设还有 m m m种数字,那么如果 M A X MAX MAX小于等于 m m m,答案就是 M A X MAX MAX。否则答案比 M A X MAX MAX小,那么答案在 [ 0 , M A X − 1 ] [0,MAX-1] [0,MAX−1]的范围内,二分即可。
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int maxn=2e5+10;
unordered_map<int,int> mp;
int a[maxn];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t,n;
cin>>t;
while(t--){
cin>>n;
mp.clear();
int MAX=0;
for(int i=1;i<=n;i++){
cin>>a[i];
mp[a[i]]++;
MAX=max(mp[a[i]],MAX);
}
int l=0,r=MAX,ans=l,sz=mp.size();
if(MAX<=sz-1){
cout<<MAX<<endl;
continue;
}
r--;
while(l<=r){
int mid=(l+r)>>1;
if(sz>=mid){
ans=mid;
l=mid+1;
}else r=mid-1;
}
cout<<ans<<endl;
}
return 0;
}
D - Anti-Sudoku(规律)
1.给出一个
9
×
9
9×9
9×9的数独棋盘,而且棋盘被分成了
9
9
9个三宫格,现在要求最多改动九个数字,使得最后每一行每一列以及分成的三宫格都有两个数字相同
2.仔细观察棋盘,以及满足了每一行每一列以及三宫格元素互不相同,那么最快的解法是将整个棋盘某个数都改为另一个数,或者找到其他规律也行
#include <iostream>
using namespace std;
char a[10][10];
int main()
{
int t;
scanf("%d",&t);
while(t--){
for(int i=1;i<=9;i++){
scanf("%s",a[i]+1);
}
for(int i=1;i<=9;i++)
if(a[1][1]!=i+'0'){
a[1][1]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[2][4]!=i+'0'){
a[2][4]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[3][7]!=i+'0'){
a[3][7]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[4][2]!=i+'0'){
a[4][2]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[5][5]!=i+'0'){
a[5][5]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[6][8]!=i+'0'){
a[6][8]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[7][3]!=i+'0'){
a[7][3]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[8][6]!=i+'0'){
a[8][6]=i+'0';
break;
}
for(int i=1;i<=9;i++)
if(a[9][9]!=i+'0'){
a[9][9]=i+'0';
break;
}
for(int i=1;i<=9;i++){
printf("%s\n",a[i]+1);
}
}
return 0;
}
E - Three Blocks Palindrome(双指针+前缀和)
easy version:
1.数据范围只有
2000
2000
2000,不难想到
O
(
26
∗
n
2
)
O(26*n^2)
O(26∗n2)的暴力算法。但是这样肯定解决不了
h
a
r
d
hard
hard的,因此在这里能想到优化,下面的将会比较好写。
2.类似于前缀和,我们统计前缀后缀每种数出现次数,
L
[
i
]
[
j
]
L[i][j]
L[i][j]表示数字
i
i
i在下标
j
j
j之前出现过多少次,同理后缀。问题就变成了,对每种数统计前缀后缀,当个数相等时,统计中间所有种类的数最多的多少种,最后取答案的最大值即可。这里还要用到双指针,尺取大法好 ,时间复杂度
O
(
26
∗
n
)
O(26*n)
O(26∗n)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2020;
int L[30][maxn],R[30][maxn];
int a[maxn];
int t,n;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
while(t--){
cin>>n;
memset(L,0,sizeof L);
memset(R,0,sizeof R);
for(int i=1;i<=n;i++){
cin>>a[i];
for(int j=1;j<=26;j++)
if(j!=a[i]) L[j][i]=L[j][i-1];
else L[j][i]=L[j][i-1]+1;
}
for(int i=n;i>=1;i--){
for(int j=1;j<=26;j++)
if(j!=a[i]) R[j][i]=R[j][i+1];
else R[j][i]=R[j][i+1]+1;
}
int ans=1;
for(int i=1;i<=26;i++){
int l=1,r=n,res=1;
while(l<r){ //注意临界条件是l<r不是l<=r,调一下样例就知道了
while(L[i][l]<R[i][r]) l++;
while(L[i][l]>R[i][r]) r--;
if(l>=r) break;
for(int j=1;j<=26;j++)
res=max(res,2*L[i][l]+L[j][r-1]-L[j][l]);
l++,r--;
}
ans=max(ans,res);
}
cout<<ans<<endl;
}
return 0;
}
hard version
实际上如果不是内存超限,上面
e
a
s
y
easy
easy原封不动的同样适用于
h
a
r
d
hard
hard,因此我们要想办法优化内存,参考了
c
f
cf
cf交的最快的几个神犇。显然我们在上面计算时只用到的是某个数出现多少次的下标,那么对于所有的数,我们可以只保存每次出现的下标,使用
v
e
c
t
o
r
vector
vector,然后尺取的时候从最左边和最右边开始,因为下标的存储是单调的,因此优化了一个后缀数组。时间复杂度
O
(
200
∗
n
)
O(200*n)
O(200∗n)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2e5+10;
int a[maxn];
int sum[205][maxn];
vector<int> v[202];
int t,n;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=200;i++) v[i].clear();
for(int i=1;i<=n;i++){
cin>>a[i];
v[a[i]].push_back(i);
for(int j=1;j<=200;j++)
sum[j][i]=sum[j][i-1]+(j==a[i]);
}
int ans=0;
for(int i=1;i<=200;i++){
int l=0,r=v[i].size()-1;
ans=max(ans,(int)v[i].size());
while(l<r){
int L=v[i][l],R=v[i][r];
for(int j=1;j<=200;j++){
ans=max(ans,2*(l+1)+sum[j][R-1]-sum[j][L]);
}
l++,r--;
}
}
printf("%d\n",ans);
}
}