前言:
第三场比前面两场难度下降了很多,打起来还算比较舒服,这场理论上来说应该可以开7题了,但是场上只写了5题,有两题是思路但是需要再仔细考虑一下的,没开出来有点可惜。
D
图片说明
思路:
签到题,范围挺小的,没多想,直接一发暴力。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e5 + 10;
typedef long long int ll;
int cal(int x){
    int res = 0;
    while(x){
        res += x % 10;
        x /= 10;
    }
    return res;
}
void solved(){
    int n;cin >> n;
    int s = cal(n);
    for(int i = n + 1; 1 ;i++){
        if(cal(i) == s){
            cout << i << endl;return ;
        }
    }
}
int main(){
    solved();
    return 0;
}

图片说明
思路:
这题挺简单的,不知道为啥大家都被卡了,因为朋友直接需要相同的糖果,就说明朋友的朋友也是自己的朋友,那么我们只需要建一个图,然后dfs,每次dfs出一个联通块,找出里面的人数和朋友的最大需要的糖果数,把他们相乘然后加起来就行了。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e6 + 10;
typedef long long int ll;
vector<int>G[maxn];
int a[maxn];
bool vis[maxn];
void dfs(int u,int &cnt,int &mm){
    vis[u] = true;
    mm = max(mm,a[u]);
    cnt += 1;
    for(int i = 0; i < G[u].size(); i++){
        int v = G[u][i];
        if(!vis[v]){
            dfs(v,cnt,mm);
        }
    }
}
void solved(){
    int n,m;scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)scanf("%d",&a[i]);
    for(int i = 1; i <= m; i++){
        int u,v;scanf("%d%d",&u,&v);
        G[u].push_back(v);G[v].push_back(u);
    }
    ll ans = 0;
    for(int i = 1; i <= n; i++){
        if(!vis[i]){
            int cnt = 0,mm = 0;
            dfs(i,cnt,mm);
            ans += mm * cnt;
        }
    }
    cout << ans << endl;
}
int main(){
    solved();
    return 0;
}

I
图片说明
思路:
很显然的一个dp,dp[i]:长度为i的从[1,i]的序列中,子序列的最大美观度。
那么我们面临的决策只有两个,那么当前字符与前面i个字符没有一个相同的即无法增加美观度,那么就是前面i个字符中有与当前i字符相同的这个时候可以增加1美丽度,在这两个决策中取max就行了。
dp[i] = max(dp[i - 1],dp[last[a[i]]] + 1)
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e6 + 10;
typedef long long int ll;
int a[maxn];
int dp[maxn];
void solved(){
    int n;cin >> n;
    for(int i = 1; i <= n; i++)cin >> a[i];
    map<int,int>mp;
    dp[1] = 0;dp[2] = (a[1] == a[2]);
    mp[a[1]] = 1;
    mp[a[2]] = 2;
    for(int i = 3; i <= n; i++){
        if(mp.count(a[i]))
            dp[i] = max(dp[i - 1],dp[mp[a[i]]] + 1); 
        else dp[i] = dp[i - 1];
        mp[a[i]] = i;
    }
    cout << dp[n] << endl;
}
int main(){
    solved();
    return 0;
}

J
图片说明
思路:
这是一个博弈题,emmmm,通过观察我们可以发现局面其实对牛妹更加有利,因为牛妹要的是偶数,那么如果最后场上有两张牌,即有三种可能,[奇数,偶数],[偶数,偶数],[奇数,奇数],那么奇数偶数偶数,偶数偶数偶数,奇数奇数偶数。也就是说只要场上只剩下两张牌,不管这些牌是什么,只要这个回合是牛妹的,牛妹必胜。
然后我们可以发现,如果最后场上只有两张牌,并且是牛牛的回合,奇数偶数奇数,奇数奇数奇数,偶数和偶数的永远是偶数,也就是说牛牛只有场上全是偶数才必败,否则必胜,然后我们可以发现如果场上的偶数牌数量<=1,牛牛必胜,否则牛牛必败(牛妹总有办法把局面变得都是偶数和牛牛)。
然后考虑一下n=1,2这题就做完了。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e6 + 10;
typedef long long int ll;
int a[maxn];
void solved(){
    int n;cin >> n;
    for(int i = 1; i <= n; i++)cin >> a[i];
    if(n == 1){
        if(a[1] & 1)cout << "NiuNiu" << endl;
        else cout << "NiuMei" << endl;
        return ; 
    }
    if(n == 2){
        if((a[1] % 2 == 1 && a[2] % 2 == 1) || (a[1] % 2 == 1 && a[2] % 2 == 0) || (a[1] % 2 == 0 && a[2] % 2 == 1))
            cout << "NiuNiu" << endl;
        else cout << "NiuMei" << endl;
    }
    if(n & 1)cout << "NiuMei" << endl;//牛妹必胜
    else {
        int f = 0;
        for(int i = 1; i <= n; i++){
            if(a[i] % 2 == 0)f++;
        }
        if(f <= 1)cout << "NiuNiu" << endl;
        else cout << "NiuMei" << endl;
    }
}
int main(){
    solved();
    return 0;
}

H
图片说明
思路:
这题不难,就是细节比较多,他们的数字相同且字符串不同只有两种方法,要么把转为十进制的>10的且不为10的一个字符拆成两个字符输出,要么把两个字符组合成这个字符输出,要么不存在方案。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e6 + 10;
typedef long long int ll;
char s[maxn];
void solved(){
    scanf("%s",s + 1);
    int len = strlen(s + 1);
    string ans;
    string res;
    for(int i = 1; i <= len; i++){
        int val = s[i] - 'a' + 1;
        if(val > 10 && val != 20){
            int l = val / 10;
            int r = val % 10;
            ans += (char)(l + 'a' - 1);
            ans += (char)(r + 'a' - 1); 
            cout << ans;
            for(++i;i <= len; i++)cout << s[i];
            return ;
        }else ans += s[i];

        if(val < 10)res += char(val + '0'); 
        else {
            res += char((val / 10) + '0');
            res += char((val % 10) + '0');
        }
    }
    //cout << res << endl;
    string s;
    for(int i = 0; i < res.size() - 1; i++){
        if(i + 2 < res.size() && res[i + 2] == '0')continue;
        if(i + 1 < res.size() && res[i] == '1' && res[i + 1] == '0'){
            s += "j";
            ++i;
            continue;
        }
        if(i + 1 < res.size() && res[i] == '2' && res[i + 1] == '0'){
            s += "t";
            ++i;
            continue;
        }
        int val = (res[i] - '0') * 10 + (res[i + 1] - '0');
        if(val > 10 && val <= 26){
            s += (char)(val + 'a' - 1);
            for(i = i + 2; i < res.size(); i++){
                if(res[i] == '1' && res[i + 1] == '0')s += "j",++i;
                else if(res[i] == '2' && res[i + 1] == '0')s += "t",++i;
                else s += ((res[i] - '0') + 'a' - 1);
            }
            cout <<  s << endl;
            return ;
        }
        else {
            s += ((res[i] - '0') + 'a' - 1);
        }
    }
    cout << "-1" << endl;
}
int main(){
    solved();
    return 0;
}

C
图片说明
思路:
数据范围很小,直接暴力dfs,枚举k次攻击,每次攻击选择圆心在[-7,7],然后计算一下圆内切,外切,相交的数量即可。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e5 + 10;
typedef long long int ll;
struct circle{
    int x,y,r;
}c[maxn];
int n,k,r;
int ans = 0;
int px[maxn],py[maxn];
void dfs(int dep){
    if(dep >= k){
        int res = 0;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= dep; j++){
                int dis = (px[j] - c[i].x) * (px[j] - c[i].x) + (py[j] - c[i].y) * (py[j] - c[i].y);
                if(dis <= 1ll * (r + c[i].r) * (r + c[i].r) || dis == (r - c[i].r) * (r - c[i].r)){
                    res ++;break;
                }
            }
        }
        ans = max(ans,res);
        return ;
    }
    for(int x = -7; x <= 7; x++){
        for(int y = -7; y <= 7; y++){
            px[dep + 1] = x;py[dep + 1] = y;
            dfs(dep + 1);
        }
    }
}
void solved(){
    cin >> n >> k >> r;
    for(int i = 1; i <= n; i++){
        scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].r);
    }
    dfs(0);
    cout << ans << endl;
}
int main(){
    solved();
    return 0;
}

F
图片说明
思路:
emmm,通过不断的wa和观察可以发现,只需要所以串的第一个#之前的字符串相同,和最后一个#的字符串相同即可,就输出-1,否则0.
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e6 + 10;
typedef long long int ll;
bool check(string a,string b){
    int n = a.size(),m = b.size();
    for(int i = 0; i < min(n,m); i++){
        if(a[i] == '#' || b[i] == '#')break;
        if(a[i] != b[i])return true;
    }
    for(int i = n - 1,j = m - 1; i && j ; i--,j--){
        if(a[i] == '#' || b[j] == '#')break;
        if(a[i] != b[j])return true;
    }
    return false;
}
void solved(){
    int n;cin >> n;
    string a,b;
    bool f = true;
    for(int i = 1; i <= n; i++){
        if(i == 1){
            cin >> a;continue;
        }else{
            b = a;cin >> a;
        }
        if(f && check(a,b)){
            f = false;
        }
    }
    if(f)cout << "-1" << endl;
    else cout << "0" << endl;
}
int main(){
    solved();
    return 0;
}

E
图片说明
思路:
其实看这个题能***不离十的猜到,应该是用线段树来做,但是可能需要对题目对一定的转换才行。
所以,我们换一个角度思考,给定一个区间[l,r],如果这个区间的每个数的最近的第一个相同的数<=r就行,否则就不行。于是想暴力肯定是不行,那么我们可以用线段树维护每个数的下一个与这个数相同值的位置,那么可以用next[i]来表示,只需要从后往前for一遍,就可以求出来了,如果没有的话next[i]=n+1,然后用线段树维护这个next数组,那么考虑删除某个数,那么这个数的上一个位置会受到影响,因为上一个数的next就是当前这个数,当前这个数不在了,那么上一个数的next应该是当前这个数的next,那么我们还需要用一个数字来维护上一个数的位置,当发生修改时。
next[x] = n + 1
last[x] = 0
next[last[x]]=next[x]
last[next[x]]=last[x]
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 6e5 + 10;
typedef long long int ll;
int Next[maxn],last[maxn];
int a[maxn];
#define mid (l + r) / 2
#define lson (rt * 2)
#define rson (rt * 2 + 1)
int m[maxn << 2];
int tot;
void build(int l,int r,int rt){
    if(l == r){m[rt] = Next[++tot];return ;}
    build(l,mid,lson);
    build(mid + 1,r,rson);
    m[rt] = min(m[lson],m[rson]);
}
void update(int l,int r,int pos,int v,int rt){
    if(l == r){m[rt] = v;return ;}
    if(pos <= mid)update(l,mid,pos,v,lson);
    else update(mid + 1,r,pos,v,rson);
    m[rt] = min(m[lson],m[rson]);
}
int query(int l,int r,int ql,int qr,int rt){
    if(l >= ql && r <= qr){return m[rt];}
    int ans = 1e9;
    if(ql <= mid)ans = min(ans,query(l,mid,ql,qr,lson));
    if(qr > mid)ans = min(ans,query(mid + 1,r,ql,qr,rson));
    return ans;
}
void solved(){
    int n,q;cin >> n >> q;
    for(int i = 1; i <= n; i++)cin >> a[i],Next[i] = n + 1,last[i] = 0;
    map<int,int>mp;
    for(int i = 1; i <= n; i++){
        if(mp.count(a[i]))
            last[i] = mp[a[i]];
        mp[a[i]] = i;
    }
    mp.clear();
    for(int i = n; i >= 1; i--){
        if(mp.count(a[i]))
            Next[i] = mp[a[i]];
        mp[a[i]] = i;
    }
    //for(int i = 1; i <= n; i++)cout << Next[i] << ' ';cout << endl;
    build(1,n,1);
    while(q--){
        int ins;scanf("%d",&ins);
        if(ins == 1){
            int x;scanf("%d",&x);
            if(last[x] != 0)
            update(1,n,last[x],Next[x],1);
            update(1,n,x,n + 1,1);
            Next[last[x]] = Next[x];
            last[Next[x]] = last[x];
            Next[x] = n + 1;
            last[x] = 0;
        }else{
            int l,r;scanf("%d%d",&l,&r);
            printf("%d\n",query(1,n,l,r,1) <= r);
        }
    }
}
int main(){
    solved();
    return 0;
}