Codeforces Round #767 (Div. 2) C~F

C

题面

在这里插入图片描述
在这里插入图片描述

思路

题目要求我们在满足字典序最大的同时有更多的数字;

不难想到, m e x ( 1 , n ) mex(1,n) mex(1,n)肯定能得到最大的mex

现在我们需要找到一个 k k k,使得这个 k k k尽可能小的同时满足 m e x ( 1 , k ) = m e x ( 1 , n ) mex(1,k) = mex(1,n) mex(1,k)=mex(1,n);

完成了这一步以后,只需要循环的执行 [ k + 1 , n ] [k+1,n] [k+1,n]这一部分即可;

基于这个思路,我们维护一个前缀、一个后缀;

前缀不断增加,后缀不断减少,直到找到这个 k k k为止;

在这里插入图片描述
然后我们就可以写出这样的代码(这份会TLE)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10;
struct Mex{
    int cnt[N];//cnt(i)表示数字i出现的次数
    set<int> st;//记录未出现的数字
    multiset<int> mulst;//记录出现过的数字
    Mex(){
        for(int i=0;i<N;++i) cnt[i] = 0;
        for(int i=0;i<N;++i) st.insert(i);
    }
    void add(int x){
        //第一次添加
        if(cnt[x] == 0){
            st.erase(x);
        }
        ++cnt[x];
        mulst.insert(x);
    }
    void del(int x){
        //只剩一个了
        if(cnt[x] == 1){
            st.insert(x);
        }
        --cnt[x];
        //不能写成mulst.erase(x) 这样是删除所有值为x的元素
        mulst.erase(mulst.find(x));
    }
    int mex(){
        return *st.begin();
    }
    void clear(){
        while(!mulst.empty()){
            del(*mulst.begin());
        }
    }
};

int a[N];
void solve(){
    int n;
    cin >> n;
    Mex prefix,suffix;
    for(int i=1;i<=n;++i){
        cin >> a[i];
        suffix.add(a[i]);
    }
    vector<int> ans;
    int mex = suffix.mex();
    for(int i=1;i<=n;++i){
        prefix.add(a[i]);
        suffix.del(a[i]);
        if(prefix.mex() == mex){
            ans.push_back(mex);
            mex = suffix.mex();
            prefix.clear();
        }
    }
    cout << ans.size() << '\n';
    for(auto x : ans){
        cout << x << ' ';
    }
    cout << '\n';
}

signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}

我们发现前缀 p r e f i x prefix prefix除了clear以外用不到del

同时我们可以预处理出每个位置suffixmex

那么我们就不需要del了,这样可以节省很多时间;并且我们可以将set换成一个vector

AC_Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10;
struct Mex{
    int cnt[N];//cnt(i)表示数字i出现的次数
    vector<int> ve;//记录进来的数
    int now = 0;//mex 因为这里只有add 因此只会增加
    Mex(){
       memset(cnt,0,sizeof cnt); 
       ve.clear();
    }
    void add(int x){
       ve.push_back(x);
       ++cnt[x];
    }
    void clear(){
        now = 0;
        for(auto x : ve) --cnt[x];
        ve.clear();
    }
    int mex(){
        while(cnt[now]) ++now;
        return now;
    }
};

int a[N];
int suffix[N];
void solve(){
    int n;
    cin >> n;
    Mex prefix,tmp;
    for(int i=1;i<=n;++i){
        cin >> a[i];
    }
    for(int i=n;i>=1;--i){
        tmp.add(a[i]);
        suffix[i] = tmp.mex();
    }
    vector<int> ans;
    int mex = suffix[1];
    for(int i=1;i<=n;++i){
        prefix.add(a[i]);
        if(prefix.mex() == mex){
            ans.push_back(mex);
            mex = suffix[i+1];
            prefix.clear();
        }
    }
    cout << ans.size() << '\n';
    for(auto x : ans){
        cout << x << ' ';
    }
    cout << '\n';
}

signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}

D

题面

在这里插入图片描述
在这里插入图片描述

思路

假设最终拼接得到是字符串是个回文串;

那么我们去掉回文串的中间一部分,得到的也是回文串;

因为给我们的字串长度至多为3;

我们分以下情况讨论;

  • 长度为1,那么肯定是回文串了;
  • 长度为2,比如ab,我们需要找到ba,无论拼前还是拼后,即abba还是baab都是回文的;
  • 长度为3,和上一条一样,最直接的就是找一个长度为3的来拼接;或者找长度为2的来拼接,这里拼前和拼后不一样,自己拿个abc来模拟即可;当然可以找长度为1的,但是我为啥不直接YES;

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <map>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

bool solve(){
    int n;
    cin >> n;
    map<string,vector<int>> mp;
    for(int i=1;i<=n;++i){
        string s;
        cin >> s;
        mp[s].push_back(i);
    }
    for(auto x : mp){
        if(x.first.size() == 1) return 1;
        if(x.first.size() == 2){
            auto y = x.first;
            reverse(y.begin(),y.end());
            if(mp.count(y)) return 1;
        }
        else{
            auto s1 = x.first.substr(0,2);
            auto s2 = x.first.substr(1,2);
            auto s3 = x.first;
            reverse(s1.begin(),s1.end());
            reverse(s2.begin(),s2.end());
            reverse(s3.begin(),s3.end());
            if(mp.count(s3)) return 1;
            if(mp.count(s1) && mp[s1].back() > mp[x.first].front()) return 1;
            if(mp.count(s2) && mp[s2].front() < mp[x.first].back()) return 1;
        }
    }
    return 0;
}

signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        bool f = solve();
        if(f) cout << "YES\n";
        else cout << "NO\n"; 
    }
    return 0;
}

E

题面

在这里插入图片描述
在这里插入图片描述

思路

因为题目只让我们求所有格子异或起来的结果,那么我们肯定不必去构造/还原每个格子是什么;

输入的方阵中每个元素是周围四个元素的异或和;

要想得到全部元素的异或和;

有两种思路;

  1. 异或起来的元素出现次数为奇数次且不遗漏;
  2. 异或起来的元素出现次数为1次且不重不漏;

我手推了 2 ∗ 2 2*2 22 4 ∗ 4 4*4 44的方阵;发现第二种思路比较容易实现;

一定是一次选定两个连续的元素,为1表示选定,为0表示不选;

在这里插入图片描述

在这里插入图片描述
然后代码的话,发现一个个格子枚举来填充就行,因为是连续的嘛,没必要一次填充两个格子;

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 1e3 + 10;

bool st[N][N];
int a[N][N];
int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};
void solve(){
    memset(st,0,sizeof st);
    int ans = 0;
    int n;
    cin >> n;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            cin >> a[i][j];
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            bool flag = 0;
            for(int k=0;k<4;++k){
                int xx = i + dx[k];
                int yy = j + dy[k];
                if(st[xx][yy]) flag = 1;
            }
            if(!flag){
                ans ^= a[i][j];
                for(int k=0;k<4;++k){
                    int xx = i + dx[k];
                    int yy = j + dy[k];
                    st[xx][yy] = 1;
                }
            }
        }
    }
    cout << ans << '\n';
}

signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}

F

题面

在这里插入图片描述
在这里插入图片描述

思路

大佬1
官方题解

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 2e3 + 10;

const int MOD = 1e9+7;

#define int ll

int f[N][N];

int qpow(int x,int y){
    int base = x,ret = 1;
    while(y){
        if(y&1){
            ret *= base;
            ret %= MOD;
        }
        base *= base;
        base %= MOD;
        y>>=1;
    }
    return ret;
}

void solve(){
    int n,m,k;
    cin >> n >> m >> k;
    for(int i=1;i<=m;++i) f[i][i] = k*i % MOD,f[i][0] = 0;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(i != j) f[i][j] = ((f[i-1][j]+f[i-1][j-1])*qpow(2,MOD-2))%MOD;
    cout << f[n][m] << '\n';
}

signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}

F2

思路

dalao视频题解

C ( n − 1 − i , m − i ) C(n-1-i,m-i) C(n1i,mi)而不是 C ( n − i , m − i ) C(n-i,m-i) C(ni,mi),我个人理解是从 f ( i − 1 , j − 1 ) f(i-1,j-1) f(i1,j1)斜着走过来;

因此贡献不应该算到与 n n n同行,应该到 n − 1 n-1 n1才对;

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 1e6 + 10;

const int MOD = 1e9+7;

#define int ll

int fact[N],infact[N];

int qpow(int x,int y){
    int base = x,ret = 1;
    while(y){
        if(y&1){
            ret *= base;
            ret %= MOD;
        }
        base *= base;
        base %= MOD;
        y>>=1;
    }
    return ret;
}

int C(int n,int m){
    ll ret = (fact[n] * infact[m])%MOD * infact[n-m];
    return ret % MOD;
}

void init(){
    fact[0] = infact[0] = 1;
    for(int i=1,inv;i<N;++i){
        fact[i] = (fact[i-1] * i)%MOD;
        inv = qpow(i,MOD-2);
        infact[i] = (infact[i-1] * inv)%MOD;
    }
}
int inv_2 = 500000004;
//typedef __int128 int128;
void solve(){
    int n,m,k;
    cin >> n >> m >> k;
    if(n == m) cout << k * n%MOD << '\n';
    else{
        int ans = 0;
        for(int i=1;i<=m;++i){
            ans %= MOD;
            //f(i,i) * (2^{n-i})^-1 * C(n-1-i,m-i);
            ans += (i%MOD) * qpow(inv_2,n-i) %MOD
            * C(n-1-i,m-i) % MOD;
        }
        cout << ans*k%MOD << '\n';
    }
}

signed main(){
    //cout << qpow(2,MOD-2) << '\n'; 500000004
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    init();
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值