Codeforces Round #780 (Div. 3) A-F2题解

Codeforces Round #780 (Div. 3)

A

题意

给定\(a\)个价值一块钱的硬币,\(b\)个价值为两块钱的硬币,问:最小的不能买的商品价值。

数据范围

思路

如果\(a=0\),那么所有奇数价值的商品都不能买,否则就可以买\(a+2*b\)以下的所有商品。

代码

int a,b;
void solve() {
    cin>>a>>b;
    if(a==0)cout<<1<<"\n";
    else cout<<a+b*2+1<<"\n";
}

B

题意

给定\(n\)种糖果的数量,每次可以吃出现频率最高的一种糖果,前后两次吃的糖果不能是同一类糖果。

数据范围

思路

考虑数量最多的两种糖果数量,如果第二高的糖果数量-第一高的数量\(>1\),那么显然不行。

代码

这个代码写的太复杂了,赛时一开始读错题了。

int n;
int maxn=0;
map<int,int>mp;
void solve() {
    cin>>n;
    maxn=0;
    mp.clear();
    for(int i=1;i<=n;i++){
        int tmp;cin>>tmp;
        mp[tmp]++;
        maxn=max(maxn,tmp);
    }
    if(maxn==1||mp[maxn]>=2){
        cout<<"YES\n";
        return ;
    }
    if(!mp[maxn-1]){
        cout<<"NO\n";
        return;
    }
    cout<<"YES\n";
}

C

题意

如果一个字符串满足以下两个条件,那么我们称它为美丽字符串:

  • 这个字符串的长度为偶数。
  • 对于每个奇数位的字符来说,\(a_i=a_{i+1}\)
    问:给定一个字符串,问最少删几个字符,使剩下的字符串为美丽字符串。

数据范围

思路

for一遍,如果当前的子串中有出现了两个一样的字符了,那么直接清空数组,可以保留的字符数量+=2,最后的答案就是原字符串长度-可以保留的字符数量。

代码

string s;
map<char,int>mp;
void solve() {
    cin>>s;
    int n=s.size();
    int ans=0;
    mp.clear();
    for(int i=0;i<n;i++){
        if(mp[s[i]]){
            mp.clear();
            ans+=2;
        }
        else {
            mp[s[i]]=1;
        }
    }
    cout<<n-ans<<"\n";
}

D

题意

给定一个长度为\(n\)且值域为\([-2,2]\)的数组,问从数组头和尾各删除多少个数,使得剩下的数组乘积最大。
ps:如果一个数组为空数组,那么这个数组的乘积默认为1.

数据范围

思路

首先,删除头尾各多少个数意味着子区间乘积最大,那么我们可以很显然的想到需要枚举子区间,然后我们会发现如果一个子区间中有0,那么这个区间的乘积也为0,那还不如整体全删掉,于是我们又可以想到的是通过0的位置进行分段。
那么对于一个子区间来说,我们只需要统计负号的数量以及区间中2的个数,当负号的个数为偶数的时候更新答案。然后需要分子区间的长度是奇数还是偶数讨论,如果是奇数那么需要考虑去掉队头和队尾,如果是偶数需要考虑去掉队头的两种情况,因为如果那个位置是-1,我们默认选了会导致答案没更新到。

代码

打的时候没脑子,又写复杂了,呜呜呜。

 
int n;
int a[N];
vector<int>v;
struct node {
    int cnt,l,r;
}ans[N<<2];
bool cmp(node a,node b){
    return a.cnt>b.cnt;
}
void solve() {
    v.clear();
    cin>>n;
    int tot=0;
    v.pb(0);
    int ff=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]==0){
            v.pb(i);
        }
        if(a[i]!=0){
            ff=1;
        }
    }
    v.pb(n+1);
    for(int i=0;i<v.size()-1;i++){
        int l=v[i]+1,r=v[i+1]-1;
        int f=0,cnt=0;
        for(int j=l;j<=r;j++){
            if(a[j]<0){
                f++;
            }
            if(abs(a[j])==2){
                cnt++;
            }
            if(f%2==0){
                ans[++tot].cnt=cnt;
                ans[tot].l=l;
                ans[tot].r=j;
            }
        }
        f=0,cnt=0;
        for(int j=l+1;j<=r;j++){
            if(a[j]<0){
                f++;
            }
            if(abs(a[j])==2){
                cnt++;
            }
            if(f%2==0){
                ans[++tot].cnt=cnt;
                ans[tot].l=l+1;
                ans[tot].r=j;
            }
        }
        f=0,cnt=0;
        for(int j=r-1;j>=l;j--){
            if(a[j]<0){
                f++;
            }
            if(abs(a[j])==2){
                cnt++;
            }
            if(f%2==0){
                ans[++tot].cnt=cnt;
                ans[tot].l=j;
                ans[tot].r=r-1;
            }
        }
        f=0,cnt=0;
        for(int j=r;j>=l;j--){
            if(a[j]<0){
                f++;
            }
            if(abs(a[j])==2){
                cnt++;
            }
            if(f%2==0){
                ans[++tot].cnt=cnt;
                ans[tot].l=j;
                ans[tot].r=r;
            }
        }
    }
    if(tot==0){
        cout<<n<<" 0"<<"\n";
        return ;
    }
    sort(ans+1,ans+1+tot,cmp);
//    for(int i=1;i<=tot;i++){
//        cout<<ans[i].cnt<<" "<<ans[i].l<<" "<<ans[i].r<<endl;
//    }
    cout<<ans[1].l-1<<" "<<n-ans[1].r<<"\n";
}

E

题意

给定一个\(n*n\)\(01\)矩阵,对于这个矩阵我们可以有5种操作:

  • 整体行往下循环,费用为\(0\)
  • 整体行往上循环,费用为\(0\)
  • 整体列往左循环,费用为\(0\)
  • 整体列往右循环,费用为\(0\)
  • 将一个点的值更改,费用为\(1\)
    问最少的费用使得这个矩阵变换为\(unitary matrix\)
    PS:\(unitary matrix\)为一个矩阵的主对角线元素全为\(1\),其他元素为\(0\).

数据范围

思路

大水题,首先统计整个矩阵中\(1\)的数量为\(cnt\),并且对于每个主对角线往一个方向循环移动,\(check\)此时的对角线上\(1\)的个数为\(tot\),那么当前情况的最少改变费用即 \((cnt-tot)+(n-tot)\).
将上述情况的答案取\(min\)即答案。

代码

int n;
char a[N][N];
void solve() {
    cin>>n;
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
            if(a[i][j]=='1'){
                cnt++;
            }
        }
    }
    int minn=n*n;
    for(int i=1;i<=n;i++){
        int sx=1,sy=i;
        int tot=0;
        for(int j=1;j<=n;j++){
            if(a[sx][sy]=='1'){
                tot++;
            }
            sx++;sy++;
            if(sy==n+1){
                sy=1;
            }
        }
        minn=min(minn,(n-tot)+(cnt-tot));
    }
    cout<<minn<<"\n";
}

F1 - F2

题意

给定一个只由'-'和'+'构成的字符串,其中'--'可以替换成一个'+',如果一个字符串满足通过替换后'+'的数量和'-'的数量相同,那么称这个字符串为美丽的。
问:该字符串中美丽的子串数量。

数据范围

\(1\)<=\(s.size()\)<=\(2*10^{5}\)

思路

把'+'看成-1,把'-'看成+1,子段和为 3 的非负整数倍即为美丽的。
现在问题就转化为了一个二维数点问题。
例如
字符串=- - + - - - +
对应的=1 2 1 2 3 4 3
那么我们知道任何一个\([l,r]\)的子区间的和为\(a[r]-a[l-1]\);
现在我们知道子段和需要等于 3 的非负整数倍,假设我们当前这位的前缀和为4,
那么满足的数应该是\(4+k*3\) \((k<=0)\)
那么我们关于%3 建立三个树状数组统计在它位置前面的且比它小的数字的数量就行。

代码

const int INF=0x3f3f3f3f;//2147483647;
const int N=2e5+50,M=1e5+50;
const ll mod=998244353;
 
const int MAXN=4e5+50;
ll tot=0;
 
int b[MAXN];
ll pre[MAXN];
 
inline ll lowbit(ll x){
    return x&(-x);
}
void updata(ll x,ll k){
    for(ll i=x;i<=MAXN;i+=lowbit(i)){
        pre[i]+=k;
    }
}
ll query(ll x){
    ll res=0;
    for(ll i=x;i>0;i-=lowbit(i)){
        res+=pre[i];
    }
    return res;
}
int n;
string s;
int a[N];
void solve() {
    cin>>n>>s;
    ll ans=0;
    for(int i=0;i<s.size();i++){
        if(s[i]=='+'){
            a[i+1]=-1+a[i];
        }
        else {
            a[i+1]=1+a[i];
        }
    }
    for(int i=0;i<3;i++){
        tot=0;
        for(int j=0;j<=(n+1)*2+3;j++)pre[j]=0;
        for(int j=0;j<=n;j++){
            if((a[j]+n+1)%3==i){
                b[++tot]=a[j]+n+1;
            }
        }
        for(int j=1;j<=tot;j++){
            ans+=query(b[j]);
            updata(b[j],1);
        }
    }
    cout<<ans<<"\n";
}
posted @ 2022-04-01 10:56  illume  阅读(51)  评论(0编辑  收藏  举报