A:小苯跑外卖

语法题,注意是上取正

#include <bits/stdc++.h>

using namespace std;

inline int read(){
    char ch = getchar();
    int x =0,f =1;
    for(;!isdigit(ch);ch =  getchar())
        if(ch == '-') f = -1;
    for(; isdigit(ch);ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f * x;
}

void slove(){
    int x= read(),y = read();
    cout << (y + x - 1) / x;
}

signed main(){
    int T = 1;
    //T = read();
    while(T --){
        slove();
    }
    return 0;
}

B:小苯的区间删除

能进行无限次操作,所以负贡献都需要删去,还是语法题。

注意多测

#include <bits/stdc++.h>
//#define ull unsigned long long int 
#define int long long int
using namespace std;

inline int read(){
    char ch = getchar();
    int x =0,f =1;
    for(;!isdigit(ch);ch =  getchar())
        if(ch == '-') f = -1;
    for(; isdigit(ch);ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f * x;
}

void slove(){
    int n = read(),k = read();
    int res = 0;
    for(int i = 1;i <= n;i ++){
        int x = read();
        if(x > 0) res += x;
    }
    cout << res << endl;
}
signed main(){
    int T = 1;
    T = read();
    while(T --){
        slove();
    }
    return 0;
}

C:小苯的数字消除

由于只有相邻两项相同时,消去这两项,不难想到括号匹配

我们先用类似括号匹配的方式将串变成 101010101,接着考虑修改操作,显然每次修改都会使得串的长度 -2 ,所以如果是用栈实现括号匹配,答案就是

注意多测

#include <bits/stdc++.h>
//#define ull unsigned long long int 
#define int long long int
#define endl '\n'
#define pb push_back
using namespace std;

const int N = 1E6 + 70,mod = 998244353,INF = 0x3f3f3f3f3f3f3f3f;

inline int read(){
    char ch = getchar();
    int x =0,f =1;
    for(;!isdigit(ch);ch =  getchar())
        if(ch == '-') f = -1;
    for(; isdigit(ch);ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f * x;
}

char s[N];

void slove(){
    int n = read();
    stack<int > sta;
    char ch;
    for(int i = 1;i <= n;i ++){
        cin >> ch;
        int x = ch - '0';
        if(sta.empty() || sta.top() != x) sta.push(x);
        else if(sta.top() == x) sta.pop();
    }
    cout << sta.size() / 2 << endl;
}
signed main(){
    int T = 1;
    T = read();
    while(T --){
        slove();
    }
    return 0;
}

D:小苯的数字集合

首先读题,发现一个重要的点,数字集合 可重集合 ,这意味着里面可以出现重复数字。

所以对于任意的 ,我们都能够通过 次操作得到 。例:

所以我们只需考虑能否通过少于 次的操作得到

详细见代码

#include <bits/stdc++.h>
//#define ull unsigned long long int 
#define int long long int
#define endl '\n'
#define pb push_back
using namespace std;

const int N = 1E6 + 70,mod = 998244353,INF = 0x3f3f3f3f3f3f3f3f;

inline int read(){
    char ch = getchar();
    int x =0,f =1;
    for(;!isdigit(ch);ch =  getchar())
        if(ch == '-') f = -1;
    for(; isdigit(ch);ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f * x;
}

char s[N];

void slove(){
    int x = read(),y = read();
    if(x == y || (x & y) == 0) cout << 1 << endl;
    else {
        int c = x & y;
        if((c & x) == 0 || (c & y) == 0 || c == x || c == y){
            cout << 2 << endl;
            return;
        }
        c = x | y;
        if((c & x) == 0 || (c & y) == 0|| c == x || c == y){
            cout << 2 << endl;
            return;
        }
        c = x ^ y;
        if((c & x) == 0 || (c & y) == 0 || c == x || c == y){
            cout << 2 << endl;
            return;
        }
        c = __gcd(x,y);
        
        if((c & x) == 0 || (c & y) == 0|| c == x || c == y){
            cout << 2 << endl;
            return;
        }
        
        cout << 3 << endl;
    }
}

signed main(){
    int T = 1;
    T = read();
    while(T --){
        slove();
    }
    return 0;
}

我不会告诉你我赛时写了两个 c == y导致我WA的

E:小苯的Polygon

赛时看到凸多边形想了一下三角形不等式就跑了

由三角形不等式我们可知,对于三角形的三条边 ,有:

即最长边小于两边之和,对于所有的凸多边形我们也有类似的结论:

记凸多边形的边集为 为凸多边形的所有边的长度之和, 为边集 中的最长边。

则有

我们可以枚举 ,用 01背包 来记录所有小于 的边所能组合成的边集

遍历一遍看看 能否组成凸多边形。

如何枚举 ? 只需保证前面的边都小于等于当前的边即可(std::sort)。

类似分析思想的小 题目 NWERC 2024 D

#include <bits/stdc++.h>
//#define ull unsigned long long int 
#define int long long int
#define endl '\n'
#define pb push_back
using namespace std;

const int N = 1E5 + 70,mod = 998244353,INF = 0x3f3f3f3f3f3f3f3f;
double exps = 1e-4;

typedef pair<int,int> PII;
typedef pair<double,double> PDD;

inline int read(){
    char ch = getchar();
    int x =0,f = 1;
    for(;!isdigit(ch);ch =  getchar())
        if(ch == '-') f = -1;
    for(; isdigit(ch);ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f * x;
}

char s[N];
int a[N];
bool f[N];

void slove(){
    int n = read();
    int sum = 0;
    for(int i = 1;i <= n;i ++) a[i]= read(),sum += a[i];
    for(int i = 1;i <= sum;i ++)f[i] = 0;
    f[0] = 1;
    sort(a + 1,a  +1 + n);
    int res = INF;
    for(int i = 1;i <= n;i ++){
        for(int j =  a[i] + 1;j <= sum;j ++){
            //S(E) - max(E) >= max(E) + 1 >  max(E)
            if(f[j]) {res = min(j + a[i] ,res);break;}
        }
        for(int j = sum;j >= a[i];j --){
            f[j] |= f[j - a[i]];
        }
    }
    if(res == INF) res = -1;
    cout << res << endl;
}

signed main(){
    int T = 1;
    T = read();
    while(T --){
        slove();
    }
    return 0;
}

F:小苯的线性dp

我们显然能够想到将一串连续的数合并起来与未合并的数比较相减。

注意到

大胆跑

我这里是采用 st表 的获取

#include <bits/stdc++.h>
//#define ull unsigned long long int 
#define int long long int
#define endl '\n'
#define pb push_back
using namespace std;

const int N = 1E6 + 70,mod = 998244353,INF = 0x3f3f3f3f3f3f3f3f;
double exps = 1e-4;

typedef pair<int,int> PII;
typedef pair<double,double> PDD;

inline int read(){
    char ch = getchar();
    int x =0,f =1;
    for(;!isdigit(ch);ch =  getchar())
        if(ch == '-') f = -1;
    for(; isdigit(ch);ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f * x;
}

int a[N],st[N][20],sum[N];
int n;
void init(){
    sum[0] = 0;
    for( int i = 1;i <= n;i ++) st[i][0] = a[i],sum[i] = sum[i - 1] + a[i];
	for( int j = 1;j <= 20;j ++){
        int t =(1 << (j - 1));
		for( int l = 1;l <= n - t;l ++){
			st[l][j] = min(st[l][j - 1],st[l + t][j - 1]);
		}
	}
}

inline int query(int l,int r){
    if(l > r) return INF;
	int k = __lg(r - l + 1);
    return min(st[l][k],st[r - (1 << k) + 1][k]);
}

void slove(){
    n = read();
    for(int i = 1;i <= n;i ++) a[i] = read();
	init();
    for(int k = 1;k < n;k ++){
        int res = 0;
        for(int i = k;i <= n;i ++){
            int t = (sum[i] - sum[i - k]) - min(query(1,i - k),query(i + 1,n));
            res = max(t,res);
        }
        cout << res << " ";
    }
    cout << 0 << endl;
}

signed main(){
    int T = 1;
    T = read();
    while(T --){
        slove();
    }
    return 0;
}

交上去一看,怎么 了?

手造数据发现类似于 1000,9000,1,10,100 ,不合并 这个数显然更优,也就是 [1000,9000],1,[10,100] 此时合并 次与合并 次的结果是一样的。

我没注意到结论,然后赛时没改出来

所以我们就可以记录上一次的答案与当前作比较取

最重要的一点是,只有当合并次数 时我们才能够与上一次的结果取,因为当 时,合并后就剩下了 个数字,当 处在中间时, 一定会合并掉

#include <bits/stdc++.h>
//#define ull unsigned long long int 
#define int long long int
#define endl '\n'
#define pb push_back
using namespace std;

const int N = 1E6 + 70,mod = 998244353,INF = 0x3f3f3f3f3f3f3f3f;
double exps = 1e-4;

typedef pair<int,int> PII;
typedef pair<double,double> PDD;

inline int read(){
    char ch = getchar();
    int x =0,f =1;
    for(;!isdigit(ch);ch =  getchar())
        if(ch == '-') f = -1;
    for(; isdigit(ch);ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f * x;
}

int a[N],st[N][20],sum[N];
int n;
void init(){
    sum[0] = 0;
    for( int i = 1;i <= n;i ++) st[i][0] = a[i],sum[i] = sum[i - 1] + a[i];
	for( int j = 1;j <= 20;j ++){
        int t =(1 << (j - 1));
		for( int l = 1;l <= n - t;l ++){
			st[l][j] = min(st[l][j - 1],st[l + t][j - 1]);
		}
	}
}

inline int query(int l,int r){
    if(l > r) return INF;
	int k = __lg(r - l + 1);
    return min(st[l][k],st[r - (1 << k) + 1][k]);
}

void slove(){
    n = read();
    for(int i = 1;i <= n;i ++) a[i] = read();
	init();
    int last = -1;
    for(int k = 1;k < n;k ++){
        //我这边的 k 是合并的区间长度,合并次数是 k - 1
        int res = 0;
        for(int i = k;i <= n;i ++){
            int t = (sum[i] - sum[i - k]) - min(query(1,i - k),query(i + 1,n));
            res = max(t,res);
        }
        if(k < n - 1)res = max(res,last);
        last = res;
        cout << res << " ";
    }
    cout << 0 << endl;
}

signed main(){
    int T = 1;
    T = read();
    while(T --){
        slove();
    }
    return 0;
}

可能有人会问 的更新为什么是正确的,因为 ,若连续合并 次比合并 次优,那么合并 的最优解一定是连续合并 在上一轮合并的状态中另找两个数合并 的两者中取 ,我扔个数据在这:

input :
1
6
1000 9000 1 9 999 9000

output :
8999 9999 10007 10007 0