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

Codeforces Round #780 (Div. 3)

A. Vasya and Coins

题目描述:

a个1块钱,b个2块钱,问不能凑出的钱的最小值是多少

思路:

  • a = 0时输出1
  • a != 0时输出a + 2 * b + 1
//Work by: Chelsea
#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int x, y, z;
ll a, b, c;
string s, t;
int tr[MAX];

void work(){
    cin >> a >> b;
    if(a == 0)cout << 1 << endl;
    else{
        cout << a + b * 2 + 1 << endl;
    }
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}
 

B. Vlad and Candies

题目描述:

n种糖果,每种糖果有a[i]个,你每次吃糖果都会挑当前数量最多的一个糖果吃,问能不能找到一种序列 s [ 1 ] , s [ 2 ] . . . s [ ∑ i = 1 n a [ i ] ] s[1],s[2]...s[\sum_{i=1}^{n}{a[i]}] s[1],s[2]...s[i=1na[i]],对于所有的i满足s[i] != s[i + 1]

思路

我们只需要比较最大的值与次大的值的差即可,如果差大于1,则不可能满足,如果差小于等于1,那就会一直吃这两个,直达数量到达第三大的那个数,就一直这么下去

注意特判n=1

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int x, y, z;
ll a, b, c;
string s, t;
int tr[MAX];

void work(){
    cin >> n;
    for(int i = 1; i <= n; ++i)cin >> tr[i];
    if(n == 1){
        if(tr[1] > 1)cout << "NO\n";
        else cout << "YES\n";
        return;
    }
    sort(tr + 1, tr + 1 + n);
    if(tr[n] - tr[n - 1] <= 1)cout << "YES\n";
    else cout << "NO\n";
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}
 

C. Get an Even String

题目描述:

给你一个字符串,你可以删掉若干个字符串,使的终串满足:

  • 长度为偶数
  • a[i] = a[i + 1], i%2=1

问最少删多少个字符?

思路:

开一个26维的数组记录每种字符出现的位置

枚举每个字符s[l],用二分找到她下一个的位置r,如果不存在r,说明这个字符该删掉了,否则我们就计算一下[l + 1, r - 1]中是否存在至少一对能匹配的字符,如果存在,我们就删掉当前字符s[l],如果不存在,我们就删掉[l 1, r - 1]中所有的字符,更新位置再继续找

用二分写起来有很多细节需要注意

//Work by: Chelsea
#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
string s, t;
vector<int>tr[30];

inline int getpos(int id, int x){
    int pp = (int)(upper_bound(tr[id].begin(), tr[id].end(), x) - tr[id].begin());
    if(pp >= tr[id].size() || tr[id][pp] <= x)return -1;
    else return tr[id][pp];
}

bool judge(int l, int r){
    for(int ll = l; ll <= r; ++ll){
        int id = s[ll] - 'a' + 1;
        int rr = getpos(id, ll);
        if(rr <= r && rr > l)return true;
    }
    return false;
}

void work(){
    for(int i = 1; i <= 26; ++i)tr[i].clear();
    cin >> s;n = (int)s.size();s = " " + s;
    for(int i = 1; i <= n; ++i){
        tr[s[i] - 'a' + 1].push_back(i);
    }
    int ans = 0;
    for(int l = 1; l <= n;){
        int id = s[l] - 'a' + 1;
        int r = getpos(id, l);
        if(r == -1 || judge(l + 1, r - 1)){
            ++ans;
            ++l;
        }
        else{
            ans += r - l - 1;
            l = r + 1;
        }
    }
    cout << ans << endl;
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}
 

D. Maximum Product Strikes Back

题目描述:

给定一个数组,数组的元素的大小输出[-2, 2]的区间,你可以删任意长度的前缀,和任意长度的后缀,要求剩下的那部分的数字乘积最大,问删除的前缀和后缀的长度可以是多少

思路:

显然,0我们肯定不选,所以我们可以根据0,将长度为n的区间分成若干个不包含0的区间,求这若干个区间的最大值即可

我们需要开一个符号的前缀和数组记录负号出现的次数,用来判断区间值的乘积的正负号,还需要一个前缀和数组记录出现的绝对值是2的数量,因为值域是[-2, 2],所以能让乘积变大的只能是正负2,我们就统计下来,方便查询区间的2的数量

对于每个区间:

  • 如果当前区间的符号是正的,就不用管,更新最大值就行
  • 否则,说明需要删掉一个负数,才能变成正的,我们可以考虑删当前区间的第一个负数,或者是最后一个负数,然后来更新最大值
#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int tr[MAX];
int fu[MAX];
int _2[MAX];
vector<int>zero;
int ans, ansl, ansr;

int getfu_from_fornt(int x){
    for(int i = x; i <= n; ++i){
        if(tr[i] < 0)return i;
    }
    return -1;
}
int getfu_from_back(int x){
    for(int i = x; i >= 1; --i){
        if(tr[i] < 0)return i;
    }
    return -1;
}

void cal(int l, int r){
    if(l > r)return;
    if((fu[r] - fu[l - 1]) % 2 == 0){
        if(ans < _2[r] - _2[l - 1]){
            ans = _2[r] - _2[l - 1];
            ansl = l;ansr = r;
        }
    }
    else{
        int ll = l, pos = getfu_from_fornt(l);
        int rr = pos - 1;
        if(ll <= rr && rr >= l && rr <= r && ll >= l && ll <= r){
            if(ans < _2[rr] - _2[ll - 1]){
                ans = _2[rr] - _2[ll - 1];
                ansl = ll;ansr = rr;
            }
        }
        
        ll = pos + 1;rr = r;
        if(ll <= rr && rr >= l && rr <= r && ll >= l && ll <= r){
            if(ans < _2[rr] - _2[ll - 1]){
                ans = _2[rr] - _2[ll - 1];
                ansl = ll;ansr = rr;
            }
        }
        
        pos = getfu_from_back(r);
        ll = l;rr = pos - 1;
        if(ll <= rr && rr >= l && rr <= r && ll >= l && ll <= r){
            if(ans < _2[rr] - _2[ll - 1]){
                ans = _2[rr] - _2[ll - 1];
                ansl = ll;ansr = rr;
            }
        }
        
        ll = pos + 1;rr = r;
        if(ll <= rr && rr >= l && rr <= r && ll >= l && ll <= r){
            if(ans < _2[rr] - _2[ll - 1]){
                ans = _2[rr] - _2[ll - 1];
                ansl = ll;ansr = rr;
            }
        }
    }
}

void work(){
    cin >> n;
    for(int i = 0; i <= n; ++i)_2[i] = fu[i] = 0;
    zero.clear();
    ans = ansl = ansr = 0;
    for(int i = 1; i <= n; ++i){
        cin >> tr[i];
        if(tr[i] == 2 || tr[i] == -2)_2[i] = 1;
        if(tr[i] == 0)zero.push_back(i);
        else if(tr[i] < 0)fu[i] = 1;
        fu[i] += fu[i - 1];
        _2[i] += _2[i - 1];
    }
    if(zero.empty()){
        cal(1, n);
    }
    else{
        cal(1, zero.front() - 1);
        for(int i = 0; i < zero.size() - 1; ++i){
            cal(zero[i] + 1, zero[i + 1] - 1);
        }
        cal(zero.back() + 1, n);
    }
    if(ans == 0)cout << n << ' ' << 0 << endl;
    else cout << ansl - 1 << ' ' << n - ansr << endl;
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}
 

E. Matrix and Shifts

题目描述:

给你一个n*n的矩阵,矩阵上只有0和1,你可以将整个矩阵的所有列进行右移,最后一列移到第一列的位置去,可以将整个矩阵的所有列左移,第一列移动到最后一列去,同样的,行也类似

你可以将矩阵上任意一个点的值反转,0变1,1变0,问先进行若干次轮转后,再最小需要进行多少次反转,才能让矩阵变成一个单位矩阵(主对角线全是1,其他全是0)

思路:

先不考虑轮转,一个01矩阵要变成单位矩阵,需要的花费应该是:(所有的1的数量 + 对角线上0的数量 - 对角线上1的数量)

所以我们要让对角线上1的数量最大化

轮转的结果其实不会改变行列中1的数量,进行轮转操作只是会选择哪条对角线,所以我们可以枚举每条对角线,求数量最大的1即可

为了方便枚举,我们可以复制一个完全一样的矩阵放到旁边:

Codeforces Round #780 (Div. 3)「ABCDEF1F2」_第1张图片

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 4000 + 50
int n, m, k, op;
char tr[MAX][MAX];

void work(){
    cin >> n;
    int ans = 0;
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            cin >> tr[i][j];tr[i][j + n] = tr[i][j];
            if(tr[i][j] == '1')++ans;
        }
    }
    int p = 0;
    for(int i = 1; i <= n; ++i){
        int num = 0;
        int y = i;
        for(int j = 1; j <= n; ++j){
            if(tr[j][y] == '1')++num;
            ++y;
        }
        p = max(p, num);
    }
    cout << ans + n - 2 * p << endl;
    
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}
 

F1. Promising String (easy version)

题目描述:

给定一个字符串,字符串只有正负号两种字符,每两个相邻的负号可以变成一个正号,问存在多少个子串,满足正负号数量匹配

思路:

easy版本的n数量很小,我们可以枚举左端点去找符合条件的右端点

假设当前有a+b-,且是匹配成功的,那一定存在一个数字p,满足 b − 2 ∗ p = a + p b - 2 * p = a + p b2p=a+p,也就是 b − a = 3 ∗ p b - a = 3 * p ba=3p,所以当(b - a) % 3 == 0 && b >= a 时可以匹配成功,暴力算就行

#include 
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k;
char tr[MAX];
void work(){
    cin >> n;
    int a, b;
    for(int i = 1; i <= n; ++i){
        cin >> tr[i];
    }
    int ans = 0;
    for(int i = 1; i <= n; ++i){
        a = b = 0;
        for(int j = i; j <= n; ++j){
            if(tr[j] == '+')++a;
            else ++b;
            if((b - a) % 3 == 0 && b >= a){
//                cout << i << ' ' << j << endl;
                ++ans;
            }
        }
    }
    cout << ans << endl;
}

int main(){
    io;
    int t;cin>>t;while(t--)work();
    return 0;
}

F2. Promising String (hard version)

题目描述:

和上面一样,不过n的范围变成了2e5

思路:

还是利用我们上面推到的东西:(b - a) % 3 = 0 && b >= a

我们用一个前缀和记录b-a的数量

现在转换成存在多少对(l, r),满足 s u m [ r ] − s u m [ l − 1 ] > = 0 sum[r] - sum[l-1]>=0 sum[r]sum[l1]>=0 ( s u m [ r ] − s u m [ l − 1 ] ) m o d 3 = 0 (sum[r] - sum[l - 1]) mod 3 = 0 (sum[r]sum[l1])mod3=0

即在mod3下,满足sum[r] = sum[l - 1], (l < r)

我们可以开三个树状数组,代表模数0,1,2

然后对于每个值sum[i],我们可以去对应的(sum[i]%3+3)%3的树上去查小于等于sum[i]的数量即可

因为存在负数且树状数组不能给0进行插入和查询,所以我们需要给每个sum[i] + n + 1

还有就是要初始化一下模数为0的0的位置,即0 + n + 1的位置

#include 
typedef long long ll;
using namespace std;
int t,n,tr[3][800005];
char s[200005];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int op)
{
    for(; x<=n+n + 1; x+=lowbit(x))
        tr[op][x]++;
}
int  getsum(int x,int op)
{
    int ans=0;
    for(;x>0;x-=lowbit(x))
        ans+=tr[op][x];
    return ans;
}
int main()
{
    cin>>t;
    while(t--)
    {
        int x=0;ll ans=0;
        cin>>n>>(s+1);
        for(int i=0;i<=2*n + 1;i++)tr[0][i]=tr[1][i]=tr[2][i]=0;
        update(n,0);
        for(int i=1; i<=n; i++)
        {
            x += (s[i]=='+'?-1:1);
            ans+=getsum(x+n+1,(x%3+3)%3);
            update(x+n+1,(x%3+3)%3);
        }
        cout<<ans<<endl;
    }
    return 0;
}

你可能感兴趣的:(cf,DIV3)