题意:
有a个1元硬币和b个2元硬币,问不能组成的最小价格是多少
思路:
没有1元硬币肯定就是1,有的话就是a+2*b+1。
#include
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,a,b;
void solve(){
scanf("%d%d",&a,&b);
if(a==0) puts("1");
else printf("%d\n",a+2*b+1);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
题意:
长度为n的序列,每次让当前序列的最大值的元素减1,如果最大值元素个数有多个,可以任意选其中一个减1,问是否可以在不对同一个位置元素进行减1操作的情况下,将全部元素都减为0
思路:
对于最初的最大值,最大值-次大值<=1肯定就是好的,因为有个伴,可以来回减,所以输出 " Y E S " "YES" "YES";否则输出 " N O " "NO" "NO"
注意特判掉只有一个元素的情况,只有一个元素那么其必须为1才能是 " Y E S " "YES" "YES"
#include
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,q,a[N];
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(n==1){
if(a[1]==1) puts("YES");
else puts("NO");
return;
}
sort(a+1,a+n+1);
if(a[n]-a[n-1]>=2) puts("NO");
else puts("YES");
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
题意:
有一个字符串,问最少删除多少个字符,保证剩下字符组成的新字符,满足对于任意 i % 2 = = 1 i\%2==1 i%2==1,有 s [ i ] = s [ i + 1 ] s[i]=s[i+1] s[i]=s[i+1]
思路:
对于每种字符,我们记录其上一次出现的位置,然后讨论是否使其保留就行了
#include
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,dp[N];
char s[N];
map<char,int>pos;
void solve(){
pos.clear();
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++){
dp[i]=dp[i-1];
if(pos[s[i]]) dp[i]=max(dp[i],dp[pos[s[i]]-1]+2);
pos[s[i]]=i;
}
printf("%d\n",len-dp[len]);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
题意:
有一个由 { − 2 , − 1 , 0 , 1 , 2 } \{-2,-1,0,1,2\} {−2,−1,0,1,2}五种元素构成的长度为n的序列,问序列前面删除多少个元素和后面删除多少个元素,使得剩下的元素的乘积最大,如果没有元素剩余,此时默认值为1
思路:
我们知道没有元素剩余时值为1,那么初始最大值为1。实际上最大值也要改为使用了多少个2
我们在序列的最前面和最后面各添加一个元素0(加边界),随后我们讨论所有相邻的两个0直接的情况,因为取了元素0,那么值就会为0,这样肯定会小于最大值。
对于相邻两个0之间的数,因为乘积足够,我们可以采用记录其中绝对值为2的个数就行了,同时记录其中负数的个数。
如果负数的个数为偶数,那么我们直接拿该段全部去和最大值比较。
如果负数的个数为奇数,那么需要分来两种情况
情况1: 只有一个负数的情况
序列 : [0][2,1,2,-1,2,1][0]
我们分成前后两部分: [2,1,2]----([-1])----[2,1]
情况2: 有多个个负数的情况
序列 : [0][2,1,2,-1,2,2,-2,1,-2,1,2,1][0]
我们分成前中后两部分: [2,1,2]----([-1])----[2,2,-2,1]----([-2])----[1,2,1]
不过我们发现,因为总的是奇数个负数。
我们选择前+([-1])+中可以得到一个正数,构成此时的新前端
我们选择中+([-2])+后可以得到一个正数,构成此时的新后端
再去和最大值比较
#include
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,q,a[N],pre[N],fu[N];
vector<int>pos0;
void solve(){
pos0.clear();
pos0.push_back(0); //加边界
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
if(a[i]==0) pos0.push_back(i);
if(abs(a[i])==2) pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1];
if(a[i]<0) fu[i]=fu[i-1]+1;
else fu[i]=fu[i-1];
}
pos0.push_back(n+1); a[n+1]=0; //加边界
int mx2=0,ansl=n,ansr=0;
int len=pos0.size();
for(int i=1;i<len;i++){
int l=pos0[i-1]+1,r=pos0[i]-1;
if(l>r) continue;
int numfu=fu[r]-fu[l-1];
int num2=pre[r]-pre[l-1];
if(numfu%2==0){
if(num2>mx2){
mx2=num2;
ansl=l-1;
ansr=n-r;
}
}
else{
int idx1=-1,idx2;
for(int j=l;j<=r;j++){
if(a[j]<0){
if(idx1==-1) idx1=j;
idx2=j;
}
}
if(idx1==idx2){
int qian2=pre[idx1-1]-pre[l-1];
int hou2=pre[r]-pre[idx1];
if(qian2>=hou2&&qian2>mx2){
mx2=qian2;
ansl=l-1;
ansr=n-idx1+1;
}
else if(hou2>=qian2&&hou2>mx2){
mx2=hou2;
ansl=idx1;
ansr=n-r;
}
}
else{
int qian2=pre[idx2-1]-pre[l-1];
int hou2=pre[r]-pre[idx1];
if(qian2>=hou2&&qian2>mx2){
mx2=qian2;
ansl=l-1;
ansr=n-idx2+1;
}
else if(hou2>=qian2&&hou2>mx2){
mx2=hou2;
ansl=idx1;
ansr=n-r;
}
}
}
}
printf("%d %d\n",ansl,ansr);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
题意:
有一个n*n的01矩阵,可以向四个方向循环移动矩阵,没有花费;使得某个位置的值去^1,花费1。
问最少花费多少,使得矩阵变成一个单位矩阵
思路:
我们记录初始每条斜线上1的个数和初始总共1的个数就行了,然后去暴力得到答案。因为斜线可以分类
n=4
1 2 3 4
4 1 2 3
3 4 1 2
2 3 4 1
#include
using namespace std;
typedef long long ll;
const int N=2e3+5;
int n;
char s[N][N];
void solve(){
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%s",s[i]);
int sum=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(s[i][j]=='1') sum++;
}
}
int mi=n*n;
for(int i=0;i<n;i++){
int num=0;
for(int j=0;j<n;j++){
if(s[j][(i+j)%n]=='1') num++;
}
mi=min(mi,sum-num+n-num);
}
printf("%d\n",mi);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
题意:
由字符’+‘和’-‘构成的长度为n字符串,有多少子字符串满足其中’+‘的数量等于’-‘的数量,
两个连续的’-‘字符可以替换成一个’+‘字符
思路:
暴力,枚举起始位置l,记录字符’+’、字符’-和’已经连续可以变换成’+‘的数量,同时记录一下当前连续’-'的数量,最后去判断就行了
#include
using namespace std;
typedef long long ll;
const int N=3e3+5;
int n;
char s[N];
void solve(){
scanf("%d",&n);
scanf("%s",s+1);
int sum=0;
for(int l=1;l<=n;l++){
int nu0=0,nu1=0,lx=0,now=0;
for(int r=l;r<=n;r++){
if(s[r]=='+'){
nu1++;
lx+=now/2;
now=0;
}
else{
now++;
nu0++;
}
if(nu0==nu1) sum++;
else if(nu1>nu0) continue;
else{
int cha=nu0-nu1;
if(cha%3==0&&cha/3<=lx+now/2) sum++;
}
}
}
printf("%d\n",sum);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
题意:
同F1
思路:
数据范围 1 < = n < = 2 e 5 1<=n<=2e5 1<=n<=2e5,将F1的暴力解法给卡掉。
根据阅读大佬博客,我们知道满足题意的一定是字符’-‘数量大于等于字符’+‘的数目,且两者差值%3=0
我们不容易得到每个区间的情况,但我们发现,我们可以采用树状数组的形式,来做一个类似状态的前缀和,我们只需要知道加入当前位置字符后,前面有多少’+‘和’-'组成的数量关系与之相同,那么他们中间的一定会满足题意,做出贡献。
我们将其每个字符转成 − 1 、 1 -1、1 −1、1加入树状数组,可能会产生负数,所以整体加上一个大于长度的正整数就行。
#include
using namespace std;
typedef long long ll;
const ll N=2e5+5;
ll n,tr[N<<1][3],pre[N],len;
char s[N];
ll lowbit(ll x){return x&(-x);}
void add(ll x,ll state){
while(x<=len){
tr[x][state]++;
x+=lowbit(x);
}
}
ll query(ll x,ll state){
ll ans=0;
while(x){
ans+=tr[x][state];
x-=lowbit(x);
}
return ans;
}
void solve(){
scanf("%lld",&n);
scanf("%s",s+1);
len=(n<<1)+1;
for(ll i=0;i<=len;i++) tr[i][0]=tr[i][1]=tr[i][2]=0;
for(ll i=1;i<=n;i++) pre[i]=pre[i-1]+(s[i]=='+'?-1:1);
ll sum=0;
for(ll i=0;i<=n;i++){
ll state=(pre[i]%3+3)%3;
sum+=query(pre[i]+n+1,state);
add(pre[i]+n+1,state);
}
printf("%lld\n",sum);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}