A. Vasya and Coins
分析
有一定数量的1元和2元的硬币,找出最小不能表示的金额。
直接输出答案,当没有1一元硬币的时候特判。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=1500;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);
int main()
{
int t;
cin>>t;
while(t--)
{
int a,b;
cin>>a>>b;
int sum=a+b*2+1;
if(a==0) cout<<1<<endl;
else
cout<<sum<<endl;
}
return 0;
}
B. Vlad and Candies
分析
n个数,每次选一个数-1,每次-1的数不能是同一个,判断这个序列是否可以这样操作。
只需判断最大的数是否能减掉就行了,所以看最大数和第二大数差值是否为1(这样最大数消掉别的数肯定也能一起消掉),特判n=1的情况。
但是之后和朋友讨论,最大数可以和好多个小数消掉,比如数据1 1 1 3,这应该是可以,但是用ac代码出来是NO,也不知道是题目没看明白还是题目问题,存疑!!
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);
int a[maxn];
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
ll sum=0;
int f=1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+1+n);
if(n==1&&a[1]!=1)
{
cout<<"NO"<<endl;
continue;
}
if(a[n]-a[n-1]<2) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
C. Get an Even String
分析
给一个字符串,求最少删掉多少个字符,可以让每个字符成为偶数。
这里采用了map容器用来存字符数,然后模拟题意就行了。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);
int main()
{
int t;
cin>>t;
while(t--)
{
string s;
int f;
cin>>s;
map<char,int> mp;
int ans=0;
for(int i=0;i<s.size();i++)
{
if(!mp[s[i]])
{
mp[s[i]]=1;
continue;
}
else {
ans+=mp.size();
ans--;
mp.clear();
}
}
ans+=mp.size();
cout<<ans<<endl;
}
return 0;
}
D. Maximum Product Strikes Back
分析
给一个序列,从左删或者从右删,使这个序列的乘积最大。
题目说,最大数不超过2,所以乘积最大,就是哪个区间内2的个数最多,然后因为有负数,所以需要判断负数个数为奇数时,从那边删掉一个负数,对乘积影响较小,直接模拟。
代码
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mk make_pair
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll INF=0x3f3f3f3f;
const double pi=acos(-1);
int a[maxn];
int n;
int sum,ansl,ansr;
int count(int l,int r)
{
int cnt=0;
for(int i=l;i<=r;i++)
{
if(a[i]<0) cnt++;
}
return cnt;
}
void count2(int l,int r)
{
int cnt=0;
for(int i=l;i<=r;i++)
{
if(abs(a[i])==2) cnt++;
}
if(cnt>sum)
{
sum=cnt;
ansl=l-1;
ansr=n-r;
}
}
void cacul(int l,int r)
{
int t=count(l,r);
if(t%2==0)
{
count2(l,r);
}
else {
int l1=l;
while(a[l1]>=0) l1++;
count2(l1+1,r);
int r1=r;
while(a[r1]>=0) r1--;
count2(l,r1-1);
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
a[n+1]=0;
ansl=n;
ansr=0;
sum=0;
for(int i=1,j=1;i<=n+1;i++)
{
if(a[i]==0)
{
cacul(j,i-1);
j=i+1;
}
}
cout<<ansl<<" "<<ansr<<endl;
}
return 0;
}
E. Matrix and Shifts
分析
给一个矩阵,对这个矩阵可以进行上下左右的滑动,有一种操作可以对一个位置取反(0变1,1变0),问最少需要多少次,可以让这个矩阵变为单位矩阵(只有对角线为1)
上下左右滑动可以通过复制数组 G[i][j] =G[i+n][j]=G[i][j+n]=G[i+n][j+n] ,枚举矩阵的左上角,固定宽度和长度就可以首先模拟矩阵上下左右滑动的效果。
对于每个矩阵来说,代价就是,所有1的个数-对角线1的个数+对角线0的个数,预处理出所有1的个数,动态维护长度为n的对角线为1,为0的个数即可。
代码
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mk make_pair
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll INF=0x3f3f3f3f;
const double pi=acos(-1);
int mp[5100][5100];
int col[2010],row[2010];
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
int cnt=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
char tt;
cin>>tt;
if(tt=='1')
{
cnt++;
mp[i][j]=mp[i+n][j]=mp[i][j+n]=mp[i+n][j+n]=1;
}
else mp[i][j]=mp[i+n][j]=mp[i][j+n]=mp[i+n][j+n]=0;
}
}
int ans=INF;
for(int i=1;i<=n;i++)
{
int cnt1=0,cnt0=0;
for(int j=0;i+j<=2*n&&1+j<=2*n;j++)
{
if(mp[i+j][1+j]==1) cnt1++;
else cnt0++;
if(j>=n-1)
{
ans=min(cnt-cnt1+cnt0,ans);
if(mp[i+j-n+1][1+j-n+1]==1) cnt1--;
else cnt0++;
}
}
}
for(int i=1;i<=n;i++)
{
int cnt1=0,cnt0=0;
for(int j=0;1+j<=2*n&&i+j<=2*n;j++)
{
if(mp[1+j][i+j]==1) cnt1++;
else cnt0++;
if(j>=n-1)
{
ans=min(cnt-cnt1+cnt0,ans);
if(mp[1+j-n+1][i+j-n+1]==1) cnt1--;
else cnt0++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
F1. Promising String (easy version)
分析
F1和F2题意一样,区别为F1的n为1≤n≤3000,F2为1≤n≤2e5,给你只含“+”,“-”的字符串,你可以把相邻两个“-”转化为“+” , 问有多少个子字符串可以通过这种操作使“+”数量等于“-”,例如“±”,“±–”,都是合法的
前缀和处理出-,+的差,暴力枚举即可。
代码
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mk make_pair
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll INF=0x3f3f3f3f;
const double pi=acos(-1);
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
string s;
cin>>n>>s;
int ans=0;
for(int i=0;i<s.size();i++)
{
int cnt=0;
for(int j=i;j>=0;j--)
{
if(s[j]=='+') cnt--;
else cnt++;
if(cnt>=0&&cnt%3==0) ans++;
}
}
cout<<ans<<endl;
}
return 0;
}
F2. Promising String (hard version)
分析
显然不能暴力枚举子字符串 , 我们来看关键的判断条件s[ j ] >= s[ i ] 和 (sum[j]-sum[i])%3 == 0, 其中后者可以转化成 sum[i] ==sum[j] (在模3的意义下)
那么做法就是: 枚举每个子字符串的终点,统计出前面sum[j] >= sum[ i ] 同时 sum[j]%3 ==sum[i] %3的数量就好,那么对于每个点 i 我们可以用三个树状数组分别标记一下sum[i] ,要查询贡献度时在相应的树状数组中用前缀和相加就可以得到符合要求起点的数量。
例如{1,2,3,4} 对于sum[0] = 1, 模3也为1 ,那么在第二个树状数组中(第一个是记录模数为0的)记录一下sum[0]已经出现过,在遍历到sum[3] = 4,模数也为1,查询第二个树状数组中1~sum[4]的前缀和,就是这个点的贡献值;
但是由于sum数组会有负数,所以最后加上一个偏移量,让所有数大于0,不影响sum[j]-sum[i])%3 == 0的判断
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e6+10;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);
int n;
int tr[maxn][3];
int sum[maxn];
char s[maxn];
int lowbit(int x)
{
return x &(-x);
}
void update(int x,int y,int m) //单点更新
{
for(int i=x;i<=maxn;i+=lowbit(i)) //x为更新的位置,y为更新后的数,n为数组最大值
tr[i][m]+=y;
}
ll getsum(int x,int m) //区间查询
{
ll ans = 0;
for(int i=x;i>=1;i-=lowbit(i))
ans += tr[i][m];
return ans;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n;
cin>>(s+1);
int minn=0;
sum[0]=0;
for(int i=1;i<=n;i++)
{
//sum[i]=sum[i-1]+(s[i]=='+'?-1:1);
if(s[i]=='+')
{
sum[i]=sum[i-1]-1;
}
else sum[i]=sum[i-1]+1;
minn=min(minn,sum[i]);
}
for(int i=0;i<=n-minn+10;i++)
{
tr[i][0]=tr[i][1]=tr[i][2]=0;
}
ll ans=0;
for(int i=0;i<=n;i++)
{
sum[i]-=minn-1;
}
for(int i=0;i<=n;i++)
{
int tt=sum[i]%3;
ans+=getsum(sum[i],tt);
update(sum[i],1,tt);
}
cout<<ans<<endl;
}
return 0;
}