Codeforces Round #780 (Div. 3)(ABCDEF1F2)

Codeforces Round #780 (Div. 3)(ABCDEF1F2)

A. Vasya and Coins

Codeforces Round #780 (Div. 3)(ABCDEF1F2)_第1张图片
题意:
有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;
}

B. Vlad and Candies

Codeforces Round #780 (Div. 3)(ABCDEF1F2)_第2张图片
题意:
长度为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;
}

C. Get an Even String

Codeforces Round #780 (Div. 3)(ABCDEF1F2)_第3张图片
题意:
有一个字符串,问最少删除多少个字符,保证剩下字符组成的新字符,满足对于任意 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;
}

D. Maximum Product Strikes Back

Codeforces Round #780 (Div. 3)(ABCDEF1F2)_第4张图片
题意:
有一个由 { − 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;
}

E. Matrix and Shifts

Codeforces Round #780 (Div. 3)(ABCDEF1F2)_第5张图片
题意:
有一个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;
}

F1. Promising String (easy version)

Codeforces Round #780 (Div. 3)(ABCDEF1F2)_第6张图片

题意:
由字符’+‘和’-‘构成的长度为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;
}

F2. Promising String (hard version)

Codeforces Round #780 (Div. 3)(ABCDEF1F2)_第7张图片
题意:
同F1
思路:
数据范围 1 < = n < = 2 e 5 1<=n<=2e5 1<=n<=2e5,将F1的暴力解法给卡掉。
根据阅读大佬博客,我们知道满足题意的一定是字符’-‘数量大于等于字符’+‘的数目,且两者差值%3=0
我们不容易得到每个区间的情况,但我们发现,我们可以采用树状数组的形式,来做一个类似状态的前缀和,我们只需要知道加入当前位置字符后,前面有多少’+‘和’-'组成的数量关系与之相同,那么他们中间的一定会满足题意,做出贡献。
我们将其每个字符转成 − 1 、 1 -1、1 11加入树状数组,可能会产生负数,所以整体加上一个大于长度的正整数就行。

#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;
}

你可能感兴趣的:(codeforces,思维,c++,算法)