Educational Codeforces Round 96 ABCDE 题解(详解)

A. Number of Apartments

题意:用3、5、7凑数,若能凑出给出方案,不能则输出-1。

思路:观察发现除了1 2 4凑不到以外其他都凑得到。那么关于方案的话,既然其他数都凑得到,我们就可以用dp的思想每次试探着来,若减去当前数还是个可以凑得到的就继续减直到等于0。

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define endl '
'
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll all[3] = {7,5,3};
ll Map[2000];
ll cnt[10];

int main()
{
    int kase;
    cin>>kase;
    Map[1] = 1, Map[2] = 1, Map[4] = 1;
    while(kase--)
    {
        ll n = read();
        mem(cnt,0);
        if(Map[n])
        {
            cout<<-1<<endl;
            continue;
        }
        while(n)
        {
            rep(i,0,2) if(n-all[i]>=0&&!Map[n-all[i]])
            {
                cnt[all[i]]++;
                n -= all[i];
                break;
            }
        }
        cout<<cnt[3]<<' '<<cnt[5]<<' '<<cnt[7]<<endl;
    }
    return 0;
}


B. Barrels

题意:有n个无限容量的水桶,现在各含a[i]的水,现在可以随便k次,问最大最小值差的最大值。

思路:水题,从次大的水桶开始倒k桶,全部倒进最大的那一桶。

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define endl '
'
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll a[maxn];

int main()
{
    int kase;
    cin>>kase;
    while(kase--)
    {
        ll n = read(), k = read();
        rep(i,1,n) a[i] = read();
        sort(a+1,a+1+n);
        int p = 1;
        ll cnt = 1;
        for(int i=n-1; i>=1&&cnt<=k; i--)
        {
            a[n] += a[i];
            cnt++;
        }
        cout<<a[n]<<endl;
    }
    return 0;
}


C. Numbers on Whiteboard

题意:每次可以把任意两个数a,b替换成(lceil {(a+b)/2} ceil),可以操作n-1次,问最后剩下的一个数能达到的最小值是多少。并且输出每一步操作。

思路:从后面大的开始,先把a[n] a[n-2]变成a[n-1],再把两个a[n-1]结合变成1个a[n-1],然后就可以一路从a[n-3]往前平推,每次都会使得值-1。

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define endl '
'
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };


typedef struct Ans
{
    ll a;
    ll b;
}A;
A ans[maxn];


int main()
{
    int kase;
    cin>>kase;
    while(kase--)
    {
        ll n = read();
        ll k = n-1;
        if(n==2)
        {
            cout<<2<<endl;
            cout<<1<<' '<<2<<endl;
            continue;
        }
        ans[1].a = n;
        ans[1].b = n-2;
        ans[2].a = n-1;
        ans[2].b = n-1;
        int p = 2;
        ll cur = n-1;
        per(i,n-3,1)
        {
            ans[++p].a = cur;
            ans[p].b = i;
            cur = (cur+i+1)/2;
        }
        cout<<cur<<endl;
        rep(i,1,p) cout<<ans[i].a<<' '<<ans[i].b<<endl;
    }
    return 0;
}


D. String Deletion

题意:给你一个01串,每一次操作必须先删掉任意一个位置,然后删除掉最长相同前缀。问最多能维持多少次这个操作。

思路:既然每次操作是对一个相同前缀下手,那我们就把每一块相同数所组成的联通块看成一个整体。同时记录他们的长度。
如1110010就可以看成
111 00 1 0
写成联通块(长度表示)
3 2 1 1
然后我们发现,会使得我们操作变少的是联通块长度为1的,我们现在想让1尽量的维持下去,即不让他在每次操作前减完自身使得要牺牲下一个联通块。
首先考虑到如果一个数大于1,那么“删除”这一步减自己就好了,不浪费别的。
而如果一个数等于1, 那我们就要考虑从它后面(轮到它当前缀时)找一些大于2的数(找2的话一减又多出个1来),这样就可以让它维持下去,删除前缀时就可以减掉当前的1而不影响后面一个。而找不到的话,那就只能减去当前这个1,然后再牺牲下一个联通块了(下一个联通块变成新的前缀)。
主要的思想就是上面说的,现在考虑怎么实现“在后面找一个大于2的数”,如果每个位置都往后找,那就是(O(n^2))的时间复杂度,肯定不行。
我们发现当前i位置往后,若找到某个位置j来删除,那么在i+1位置,肯定也是从j开始往后找的,也就是前j-1个位置既然都不满足i位置,那么肯定也不满足i+1位置。这样我们只需要用双指针走一遍这个数组即可。时间复杂度(O(2n))

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define endl '
'
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

string s;
ll block[maxn];
int p = 0;
ll solve()
{
    int q = 1;
    ll ans = 0;
    bool flag = 0;
    rep(i,1,p)
    {
        if(flag)
        {
            ans++;
            i += 1;
            continue;
        }
        if(block[i]>1) ans++;
        else
       {
            while(q<i||(q<=p&&block[q] <= 1)) q++;
            if(q>p)
            {
                flag = 1;
                ans++;
                i += 1;
            }
            else
            {
                block[q]--;
                ans++;
            }
       }
    }
    return ans;
}

int main()
{
    int kase;
    cin>>kase;
    while(kase--)
    {
        p = 0;
        ll n = read();
        string s;
        cin>>s;
        block[++p] = 1;
        rep(i,1,n-1)
        {
            if(s[i]==s[i-1]) block[p]++;
            else block[++p] = 1;
        }
        cout<<solve()<<endl;
    }
    return 0;
}


E. String Reversal

题意: 给一个字符串,每次可以相邻位置交换,问最少多少次可以使得字符串变成原来的颠倒字符串。

思路:显然有比直接一步步交换过去更优的策略。
比如acbac,我们想让第一个a变到最后一个位置去,完全不需要第一个位置的a动,只需要最靠近右边的a和c交换即可,一步到位。
所以策略就是每次都让最靠近右边的,和当前字符相同的字符移动就好。
但是这样会有个问题,就是有些位置被移动过,它左边的数再经过它往右交换时,距离就要减去这个空位。那怎么维护这个东西呢?
我们把这个“空位”产生的影响描述出来就是,一开始找到第一个位置的最靠右的相同字符,位置为p1,丢到最后,距离是n-p1。
然后第二个位置最靠右相同字符的位置p2, 若p2<p1,即p1这个空位对p2产生影响了,那就是n-p2-1。同理若p3<p2, 那就是n-p3-2。
所以就只需要统计p位置后面有多少空位即可,然后减去这个数。
这个时候树状数组的作用就来了。
为了方便,我们反过来看,从最后开始往前丢数,一开始是p1-1, 然后若p2>p1, 就p2-1-1,其次是p3-2-1(后面的-1是到1的距离,再减的就是空位个数)。所以我们的空位对计数是负贡献,把一开始[1,n]的区间每个位置都置为1, 一旦产生一个位置p产生空位,就在p这个位置-1,这样前缀和即表示(p-空位个数),每次的贡献就是前缀和-1。

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define endl '
'
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll n;
string s;
queue<ll> q[50];
ll c[maxn];

inline int id(int p)
{
    return s[p-1] - 'a' + 1;
}

inline void add(int p, ll val)
{
    for(int i=p; i<=n; i+=lowbit(i)) c[i] += val;
}

inline ll ask(int p)
{
    ll ans = 0;
    for(int i=p; i>=1; i-=lowbit(i)) ans += c[i];
    return ans;
}

int main()
{
    n = read();
    cin>>s;
    rep(i,1,n) q[id(i)].push(i), add(i,1);
    ll ans = 0;
    per(i,n,1)
    {
        int pos = q[id(i)].front();
        q[id(i)].pop();
        ans += ask(pos)-1; add(pos, -1);
    }
    cout<<ans<<endl;
    return 0;
}


原文地址:https://www.cnblogs.com/Bgwithcode/p/13805677.html