$ACM$ 期末考试

\(ACM\) 期末考试

目录
  • $ACM$ 期末考试
    • $A.\ Alphabet$
      • 题意
      • 题解
      • $code$
    • $B.\ Barbells$
      • 题意
      • 题解
      • $code$
    • $C,\ Buggy\ Robot$
      • 题意
      • 题解
      • $code$
      • 后记
    • $D.\ Cameras$
      • 题意
      • 题解
      • $code$
    • $E.\ Contest Score$
      • 题意
      • 题解
      • $code$
    • $F.\ Equality$
      • 题意
      • 题解
      • $code$
    • $G.\ Gravity$
      • 题意
      • 题解
      • $code$
    • $H.\ Islands$
      • 题意
      • 题解
      • $code$
      • 后记
    • $I.\ Mismatched\ Socks$
    • $J.\ Postman$
      • 题意
      • 题解
      • $code$
    • $K.\ Six Sides$
      • 题意
      • 题解
      • $code$
    • $L.\ Three\ Square$
      • 题意
      • 题解
      • $code$
    • $M.\ Zigzag$
      • 题意
      • 题解
      • $code$
    • $N.\ Paint$
      • 题意
      • 题解
      • $code$
      • 后记
    • $O.\ Shopping$
      • 题意
      • 思路
      • $code$
      • 后记
    • $P.\ Contest\ Strategy$

\(A.\ Alphabet\)

题意

给定一个由小写字母组成的字符串 \(s\),可以在任意位置插入任意数量的小写字母
计算最少插入多少小写字母,使得新字符串 \(s'\) 中含有子序列 abcdefghijklmnopqrstuvwxyz

\(|s| < 50\)

题解

即求字符串中的最长上升子序列

定义 \(dp[i]\) 为到 \(i\) 位置所具备的最长上升子序列,则 \(dp[i] = max\left\{dp[j] + 1,\ j < i\right\}\)

\(O(n^2)\) 暴力转移

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}

char str[107];
int dp[107];
int main()
{
    scanf("%s", str + 1);
    int L = strlen(str + 1);
    for(int i = 1; i <= L; ++i){
        dp[i] = 1;
        for(int j = 1; j < i; ++j)
            if(str[j] < str[i]) dp[i] = max(dp[i], dp[j] + 1);
    }
    int ans = 0;
    //for(int i = 1; i <= L; ++i) cout << i << ' ' << dp[i] <

\(B.\ Barbells\)

题意

\(n\) 个杠铃和 \(m\) 个杠铃片,\(n,\ m\leq 14\)

在满足杠铃两端重量一致的前提下,计算这些器件一共可以组和出多少种不同的重量

题解

双重二进制枚举杠铃片的选则,\(check\) 重量是否一致,用与运算判断是否为独立集合即可

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
int n, m;
LL a[20], b[20];
set st;
int main()
{
    n = read(), m = read();
    for(int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    for(int i = 0; i < m; ++i) scanf("%lld", &b[i]);
    for(int i = 0; i < (1 << m); ++i) {
        for(int j = 0; j < (1 << m); ++j) {
            if((i & j) == 0) {
                LL x = 0, y = 0;
                for(int k = 0; k < m; ++k) {
                    if(i & (1 << k)) x += b[k];
                    if(j & (1 << k)) y += b[k];
                }
                if(x == y) st.insert(x + y);
            }
        }
    }
    set ans;
    for(auto i: st) {
        for(int j = 0; j < n; ++j)
            ans.insert(i + a[j]);
    }
    for(auto i: ans) cout << i << endl;
    return 0;
}

\(C,\ Buggy\ Robot\)

题意

在一个 \(n\times m\) 的二维迷宫上,给一个机器人输入一串 URDL 的指令 \(s,\ |s| \leq 50\),操纵其从入口走到出口

机器人按照指令移动

  • 若当前指令使得机器人遇到障碍物 # 或者走出边界,那么机器人会忽视当前指令
  • 若走到终点,剩下的所有指令作废

已知指令错误,现在允许在任意位置插入新指令,也允许删除任意指令,亦可以不改变指令

计算最小的修改次数,使得机器人可以走到终点

\(n,\ m,\ |s|\leq 50\)

题解

定义 \(dp[i][j][k]\) 为执行第 \(k\) 条指令后,机器人走到 \((i,\ j)\) 位置时所需要修改的最小次数

考虑状态转移

  • 若删除当前指令,则 \(dp[i][j][k + 1] = min(dp[i][j][k + 1],\ dp[i][j][k] + 1)\)
  • 若在当前位置增添指令,则 \(dp[i'][j'][k] = min(dp[i'][j'][k],\ dp[i][j][k] + 1)\)
  • 若什么也不做,则 \(dp[i'][j'][k + 1] = min(dp[i'][j'][k + 1],\ dp[i][j][k])\)

\(bfs\) 暴力转移

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = (1 << 20) + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}

int n, m, mp[10];
char g[100][100], str[100];
int dp[100][100][100];
struct node
{
    int x, y, pos;
    node() {}
    node(int _x, int _y, int _pos) {x = _x, y = _y, pos = _pos;}
};
bool check(int x, int y) {return x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] != '#';}
void bfs(int sx, int sy, int ex, int ey, int L)
{
    queue q;
    dp[sx][sy][0] = 0;
    q.push(node(sx, sy, 0));
    while(!q.empty()) {
        node temp = q.front(); q.pop();
        for(int i = 1; i <= 4; ++i) {
            int tx = temp.x + DX[i], ty = temp.y + DY[i];
            if(check(tx, ty) && dp[tx][ty][temp.pos] > dp[temp.x][temp.y][temp.pos] + 1) {
                    dp[tx][ty][temp.pos] = dp[temp.x][temp.y][temp.pos] + 1;
                    q.push(node(tx, ty, temp.pos));
                }
        }
        if(temp.pos < L) {
            if(dp[temp.x][temp.y][temp.pos + 1] > dp[temp.x][temp.y][temp.pos] + 1) {
                dp[temp.x][temp.y][temp.pos + 1] = dp[temp.x][temp.y][temp.pos] + 1;
                q.push(node(temp.x, temp.y, temp.pos + 1));
            }
            int tpos = temp.pos + 1;
            int tx = temp.x + DX[mp[str[tpos]]], ty = temp.y + DY[mp[str[tpos]]];
            if(!check(tx, ty)) tx = temp.x, ty = temp.y;
            if(dp[tx][ty][tpos] > dp[temp.x][temp.y][temp.pos]) {
                dp[tx][ty][tpos] = dp[temp.x][temp.y][temp.pos];
                q.push(node(tx, ty, tpos));
            }
        }
    }
}
int main()
{
    int sx, sy, ex, ey;
    mp['U'] = 1, mp['R'] = 2, mp['D'] = 3, mp['L'] = 4;
    scanf("%d %d", &n, &m);
    memset(dp, inf, sizeof(dp));
    for(int i = 1; i <= n; ++i)
        scanf("%s", g[i] + 1);
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            if(g[i][j] == 'R') sx = i, sy = j;
            if(g[i][j] == 'E') ex = i, ey = j;
        }
    }
    scanf("%s", str + 1);
    int L = strlen(str + 1);
    bfs(sx, sy, ex, ey, L);
    int ans = inf;
    for(int i = 1; i <= L; ++i) ans = min(ans, dp[ex][ey][i]);
    printf("%d\n", ans);
    return 0;
}

后记

感觉这题相当难想,也相当难写,需要仔细体会

\(D.\ Cameras\)

题意

\(n\) 个房子,其中 \(k\) 个房子有灯

现在要求每 \(r\) 个连续房子至少得有两个房子有灯,计算需要增加的灯的个数

\(n\leq 100000,\ 0\leq k\leq n\)

题解

贪心,优先往右边的房子放灯

嵌套循环寻找右边第一个没有灯的位置,该位置灯的数量加一,注意这个嵌套循环最多循环三次,复杂度最多带个常数 \(3\)

要求快速查询区间内灯的个数,以及单点修改,所以考虑用树状数组进行维护

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
int n, k, r, a[N];
//BIT
int c[N];
int lowbit(int x) {return x & (-x);}
void upd(int *c, int i, int val)
{
    while(i <= n){
        c[i] += val;
        i += lowbit(i);
    }
}
int query(int *c, int i)
{
    int ans = 0;
    while(i > 0) {
        ans += c[i];
        i -= lowbit(i);
    }
    return ans;
}
int main()
{
    n = read(), k = read(), r = read();
    for(int i = 1; i <= k; ++i) {
        int x = read();
        a[x] = 1;
        upd(c, x, 1);
    }
    int ans = 0;
    for(int i = 1; i <= n - r + 1; ++i) {
        int num = query(c, i + r - 1) - query(c, i - 1);
        if(num < 2) {
            int t = 0;
            for(int j = i + r - 1; j >= i; --j) {
                if(!a[j]) upd(c, j, 1), t++;
                if(t == 2 - num) break;
            }
            ans += 2 - num;
        }
    }
    cout << ans << endl;
    return 0;
}

\(E.\ Contest Score\)

题意

设某次 \(xcpc\) 共有 \(n\) 个题目,现给定一个队伍对于每道题的解题时间 \(t_{i}\),以及竞赛策略

  • \(1.\) 先读前 \(k\) 个题
  • \(2.\) 选择花费时间最少的题目进行解答
  • \(3.\) 如果还有题目剩余,读下一个题目
  • \(4.\) 如果还有没做完的题目,回到 \(2\)

假设读题不花费时间,输出罚时

\(1\leq k\leq n\leq 300,\ 1\leq t_{i}\leq 1000000\)

题解

签到,优先队列模拟即可

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
int n, k;
LL t[500];
priority_queue, greater > pq;
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> k;
    LL time = 0, ans = 0;
    int cnt = k + 1;
    for(int i = 1; i <= n; ++i) cin >> t[i];
    for(int i = 1; i <= k; ++i) pq.push(t[i]);
    while(!pq.empty()) {
        time += pq.top(); ans += time; pq.pop();
        if(cnt <= n) pq.push(t[cnt++]);
    }
    cout << ans << endl;
    return 0;
}

\(F.\ Equality\)

题意

给定 \(a + b = c\),判断等式是否成立

题解

签到,格式化读入之后判断即可

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}

int main()
{
    int a, b, c;
    scanf("%d + %d = %d", &a, &b, &c);
    puts(a + b == c ? "YES" : "NO");
    return 0;
}

\(G.\ Gravity\)

题意

给定一个 \(n\times m\) 的二维格点图,模拟苹果受重力的掉落情况

\(1\leq n,\ m\leq 50\)

题解

签到,模拟即可

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}

int n, m;
char g[100][100];
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
        scanf("%s", g[i] + 1);
    for(int j = 1; j <= m; ++j) {
        for(int i = n; i >= 1; --i) {
            if(g[i][j] == 'o') {
                g[i][j] = '.';
                int f = 0;
                for(int k = i; k <= n; ++k)
                    if(g[k][j] == 'o' || g[k][j] == '#') {g[k - 1][j] = 'o'; f = 1; break;}
                if(!f) g[n][j] = 'o';
            }
        }
    }
    for(int i = 1; i <= n; ++i)
        printf("%s\n", g[i] + 1);
    return 0;
}

\(H.\ Islands\)

题意

给定一个 \(n\times m\) 的二维格点图,\(L\) 表示陆地,\(W\) 表示海洋,\(C\) 表示云朵,云朵既可以变成海洋也可以变成陆地

恰当地变换 \(C\) 的形态,计算图中最少有多少陆地连通块

\(1\leq n,\ m\leq 50\)

题解

把所有与 \(L\) 相连通的 \(C\) 全部变成 \(L\),因为这样连通块个数才不会增加

\(dfs\) 即可

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}

int n, m, vis[100][100];
char g[100][100];
bool check(int x, int y) {return x >= 1 && x <= n && y >= 1 && y <= m;}
void dfs(int x, int y)
{
    vis[x][y] = 1;
    for(int i = 1; i <= 4; ++i) {
        int tx = x + DX[i], ty = y + DY[i];
        if(!vis[tx][ty] && check(tx, ty) && g[tx][ty] != 'W') dfs(tx, ty);
    }
}
int main()
{
    //ios::sync_with_stdio(false);
    cin >> n >> m;
    int ans = 0;
    for(int i = 1; i <= n; ++i)
        scanf("%s", g[i] + 1);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
            if(!vis[i][j] && g[i][j] == 'L') ans++, dfs(i, j);
    cout << ans << endl;
    return 0;
}

后记

血的教训,开启 ios::sync_with_stdio(false); 语句之后,不能使用 \(scanf,\ printf\),否则会出错

\(I.\ Mismatched\ Socks\)

\(J.\ Postman\)

题意

一维数轴上分布了 \(n\) 个点,对应的坐标为 \(x_{i}\),所需要的邮件数量为 \(m_{i}\)

邮递员每次从 \(x = 0\) 处出发,携带 \(k\) 个邮件,来回送货

请计算邮递员送完所有点需要的信所走的最短路

\(1\leq n\leq 1000,\ 1\leq k,\ |x_{i}|,\ m_{i}\leq 10000000\)

题解

贪心,正负点分开送,每次优先送最远的点,模拟即可

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e3 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
LL n, k;
struct data
{
    LL a, b;
    data() {}
    data(LL _a, LL _b) {a = _a, b = _b;}
    bool operator < (const data &x) const {return a < x.a;}
}d1[N], d2[N];
 
LL getAns(data *d, int nn)
{
    LL ans = 0;
    for(int i = nn; i >= 1; --i) {
        LL dis = 2LL * d[i].a;
        LL num = d[i].b;
        ans += (num / k) * dis;
        if(num % k) {
            ans += dis;
            LL left = k - (num % k);
            for(int j = i - 1; j >= 1; --j) {
                if(d[j].b >= left) {d[j].b -= left; break;}
                else left -= d[j].b, d[j].b = 0;
            }
        }
    }
    return ans;
}
int main()
{
    scanf("%lld %lld", &n, &k);
    int n1 = 0, n2 = 0;
    for(int i = 1; i <= n; ++i) {
        LL x, y;
        scanf("%lld %lld", &x, &y);
        if(x >= 0) d1[++n1] = data(x, y);
        else d2[++n2] = data(-x, y);
    }
    sort(d1 + 1, d1 + 1 + n1);
    sort(d2 + 1, d2 + 1 + n2);
    LL ans1 = getAns(d1, n1);
    LL ans2 = getAns(d2, n2);
    cout << ans1 + ans2 << endl;
    return 0;
}
/*
7 1
9400000 10000000
9500000 10000000
9600000 10000000
9700000 10000000
9800000 10000000
9900000 10000000
10000000 10000000
*/

\(K.\ Six Sides\)

题意

俩人掷俩六面骰子

  • 若第一个人的点数大,那么第一个人获胜
  • 若平局,继续抛
  • 若第一个人的点数小,那么第一个人输、

计算第一个人赢的数学期望

题解

\(a\) 为玩家 \(1\) 点数大于玩家 \(2\) 点数的实验数

\(b\) 为玩家 \(1\) 点数小于玩家 \(2\) 点数的实验数

\(a = b = 0\),则 \(E = 0\),否则 \(E = \frac{a}{a + b}\)

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
int a[10], b[10];
int main()
{
    for(int i = 1; i <= 6; ++i) cin >> a[i];
    for(int i = 1; i <= 6; ++i) cin >> b[i];
    int cnt1 = 0, cnt2 = 0;
    for(int i = 1; i <= 6; ++i) {
        for(int j = 1; j <= 6; ++j) {
            if(a[i] > b[j]) cnt1++;
            else if(a[i] < b[j]) cnt2++;
        }
    }
    if(cnt1 == cnt2 && !cnt1) puts("0.00000");
    else printf("%.5f\n", double(cnt1) / double(cnt1 + cnt2));
    return 0;
}

\(L.\ Three\ Square\)

题意

给定 \(3\) 个长宽确定的长方形,判断是否可以组成一个正方形

题解

三个长方形中的最长边一定是正方形的边长,以此为依据分类讨论

一共 \(6\times(4 + 4) = 48\) 种情况

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
int a[10], b[10];
 
bool check1(int a1, int b1, int a2, int b2, int a3, int b3)
{
    //cout << a1 << ' ' << b1 << ' ' << a2 << ' ' << b2 << ' ' << a3 << ' ' << b3 << endl;
    return (a1 == a2 + a3 && b2 == b3 && a1 == b1 + b3 || a1 == a2 && a1 == a3 && a1 == b1 + b2 + b3);
}
int main()
{
    int maxx = 0;
    for(int i = 1; i <= 3; ++i) cin >> a[i] >> b[i], maxx = max(maxx, max(a[i], b[i]));
    int f1 = 0, f2 = 0;
    if(a[1] == maxx) {
        f1 = check1(a[1], b[1], a[2], b[2], a[3], b[3]); f2 |= f1;
        f1 = check1(a[1], b[1], a[2], b[2], b[3], a[3]); f2 |= f1;
        f1 = check1(a[1], b[1], b[2], a[2], a[3], b[3]); f2 |= f1;
        f1 = check1(a[1], b[1], b[2], a[2], b[3], a[3]); f2 |= f1;
    }
    if(b[1] == maxx) {
        f1 = check1(b[1], a[1], a[2], b[2], a[3], b[3]); f2 |= f1;
        f1 = check1(b[1], a[1], a[2], b[2], b[3], a[3]); f2 |= f1;
        f1 = check1(b[1], a[1], b[2], a[2], a[3], b[3]); f2 |= f1;
        f1 = check1(b[1], a[1], b[2], a[2], b[3], a[3]); f2 |= f1;
    }
    if(a[2] == maxx) {
        f1 = check1(a[2], b[2], a[1], b[1], a[3], b[3]); f2 |= f1;
        f1 = check1(a[2], b[2], a[1], b[1], b[3], a[3]); f2 |= f1;
        f1 = check1(a[2], b[2], b[1], a[1], a[3], b[3]); f2 |= f1;
        f1 = check1(a[2], b[2], b[1], a[1], b[3], a[3]); f2 |= f1;
    }
    if(b[2] == maxx) {
        f1 = check1(b[2], a[2], a[1], b[1], a[3], b[3]); f2 |= f1;
        f1 = check1(b[2], a[2], a[1], b[1], b[3], a[3]); f2 |= f1;
        f1 = check1(b[2], a[2], b[1], a[1], a[3], b[3]); f2 |= f1;
        f1 = check1(b[2], a[2], b[1], a[1], b[3], a[3]); f2 |= f1;
    }
    if(a[3] == maxx) {
        f1 = check1(a[3], b[3], a[2], b[2], a[1], b[1]); f2 |= f1;
        f1 = check1(a[3], b[3], a[2], b[2], b[1], a[1]); f2 |= f1;
        f1 = check1(a[3], b[3], b[2], a[2], a[1], b[1]); f2 |= f1;
        f1 = check1(a[3], b[3], b[2], a[2], b[1], a[1]); f2 |= f1;
    }
    if(b[3] == maxx) {
        f1 = check1(b[3], a[3], a[2], b[2], a[1], b[1]); f2 |= f1;
        f1 = check1(b[3], a[3], a[2], b[2], b[1], a[1]); f2 |= f1;
        f1 = check1(b[3], a[3], b[2], a[2], a[1], b[1]); f2 |= f1;
        f1 = check1(b[3], a[3], b[2], a[2], b[1], a[1]); f2 |= f1;
    }
    puts(f2 ? "YES" : "NO");
    return 0;
}
/*
2 6
2 6
2 6
*/

\(M.\ Zigzag\)

题意

给定一个长度为 \(50\) 的序列,计算其中最长的 \(zigzagging\) 子序列

\(zigzagging\) 子序列定义为:递增递减交错的序列

例如:\(1,\ 3,\ 2,\ 6,\ 5\)

\(n\leq 50\)

题解

类似于最长上升子序列

定义 \(dp1[i]\) 表示到第 \(i\) 位置为止,且第 \(i\) 位置为增长趋势的 \(zigzagging\ subsequences\) 的最长长度

定义 \(dp2[i]\) 表示到第 \(i\) 位置为止,且第 \(i\) 位置为下降趋势的 \(zigzagging\ subsequences\) 的最长长度

考虑状态转移

\[dp1[i] = max\left\{dp1[i],\ dp2[j] + 1,\ j < i\right\} \]

\[dp2[i] = max\left\{dp2[i],\ dp1[j] + 1,\ j < i\right\} \]

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 1e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
int n, a[100], dp1[100], dp2[100];
int main()
{
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    int ans = 0;
    for(int i = 1; i <= n; ++i){
        dp1[i] = dp2[i] = 1;
        for(int j = 1; j < i; ++j) {
            if(a[j] > a[i]) dp1[i] = max(dp1[i], dp2[j] + 1);
            if(a[j] < a[i]) dp2[i] = max(dp2[i], dp1[j] + 1);
        }
    }
    for(int i = 1; i <= n; ++i) ans = max(ans, max(dp1[i], dp2[i]));
    cout << ans << endl;
    return 0;
}

\(N.\ Paint\)

题意

给定长为 \(n\) 的序列,以及 \(k\) 个子区间 \([a_{i},\ b_{i}]\)

选取若干个不相交的子区间,使得未被覆盖的元素个数最少

\(1\leq n\leq 10^{18},\ 1\leq k\leq 200000\)

题解

\(k\) 个区间按照右端点升序排序,左端点降序/升序排序

定义 \(dp1[i]\) 表示,以第 \(i\) 个区间为终点区间,所得到的最少的未被覆盖的元素个数

定义 \(dp2[i] = max\left\{dp1[j],\ j < i\right\}\)

考虑状态转移,二分查找最后一个区间 \(j\),使得 \(b_{j} < a_{i}\)

\(dp1[i] = max(dp1[i],\ dp2[j] + a_{i} - b_{i} + 1]\)

更新 \(dp2[i] = max(dp2[i - 1],\ dp1[i])\)

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 2e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
LL n;
int k;
LL dp1[N], dp2[N];
struct node
{
    LL L, R;
    node() {}
    node(LL _L, LL _R) {L = _L, R = _R;}
}d[N];
bool cmp(node x, node y)
{
    if(x.R == y.R) return x.L > y.L;
    return x.R < y.R;
}
int main()
{
    scanf("%lld %d", &n, &k);
    for(int i = 1; i <= k; ++i) {
        LL x, y;
        scanf("%lld %lld", &x, &y);
        d[i] = node(x, y);
    }
    sort(d + 1, d + 1 + k, cmp);
    for(int i = 1; i <= k; ++i) {
        int p = lower_bound(d + 1, d + 1 + k, node(INF, d[i].L), cmp) - 1 - d;
        dp1[i] = d[i].R - d[i].L + 1 + dp2[p];
        dp2[i] = max(dp2[i - 1], dp1[i]);
    }
    //for(int i = 1; i <= k; ++i)
        //cout << d[i].L << ' ' << d[i].R << ' ' << dp1[i] << ' ' << dp2[i] << endl;
    LL ans = INF;
    for(int i = 1; i <= k; ++i) ans = min(ans, n - dp1[i]);
    cout << ans << endl;
    return 0;
}
/*
15 5
1 4
5 6
6 8
8 12
12 15
*/

后记

在结构体数组里面用 lower_bound 需要使用第四个参数,自定义 cmp 比较函数,这是一个新姿势

\(O.\ Shopping\)

题意

\(n\) 个货物,每个货物有无限多个,价格为 \(a_{i}\)

\(q\) 个客户,每人有 \(v_{i}\) 的资金,他们从 \(l_{i}\) 逛到 \(r_{i}\),对于每一种货物,他们都会买到买不起为止

请计算每人剩余的资金

\(1\leq n,\ q\leq 200000,\ 1\leq a_{i},\ v_{i}\leq 10^{18},\ 1\leq l_{i},\ r_{i}\leq n\)

思路

记当前在 \(pos\) 位置,资金还剩余 \(x\)

问题等价于在 \([pos,\ r_{i}]\) 内寻找第一个不大于 \(x\) 的位置

由于是无序序列,考虑用线段树求解,复杂度套个 \(log_{2}{n}\)

由于取模操作,每次购买物品资金至少减少一半,所以最多购买 \(log_{2}v_{i}\) 件物品

总的复杂度为 \(O(qlognlogv_{i})\)

\(code\)

#include 
#define fi first
#define se second
#define pii pair
#define pb push_back
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 11092019;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 2e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
    char c = getchar();
    int ans = 0, f = 1;
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
    return ans * f;
}
 
LL n, q, a[N];
struct node
{
    LL l, r, minn;
}t[N * 4];
void pushup(int rt)
{
    t[rt].minn = min(t[rt * 2].minn, t[rt * 2 + 1].minn);
}
void build(int L, int R, int rt){
    t[rt].l = L, t[rt].r = R;
    if(L == R){
        t[rt].minn = a[L];
        return;
    }
    int mid = (L + R) / 2;
    build(L, mid, rt * 2);
    build(mid + 1, R, rt * 2 + 1);
    pushup(rt);
}
int query(int L, int R, LL val, int rt)
{
    //cout << L << ' ' << R << endl;
    if(t[rt].l == t[rt].r){
        if(t[rt].minn <= val) return t[rt].l;
        else return -1;
    }
    int mid = (t[rt].l + t[rt].r) / 2;
    int ans = -1;
    if(L <= mid && t[rt * 2].minn <= val) {
        ans = query(L, R, val, rt * 2);
        if(ans == -1){
            if(mid < R && t[rt * 2].minn <= val) ans = query(L, R, val, rt * 2 + 1);
        }
        return ans;
    }
    else if(mid < R && t[rt * 2 + 1].minn <= val) return query(L, R, val, rt * 2 + 1);
    return -1;
}
int main()
{
    scanf("%lld %lld", &n, &q);
    for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
    build(1, n, 1);
    while(q--) {
        LL m;
        int L, R;
        scanf("%lld %d %d", &m, &L, &R);
        int p = L;
        while(1) {
            m %= a[p];
            p = query(p + 1, R, m, 1);
            if(p == -1) break;
        }
        cout << m << endl;
    }
    return 0;
}
/*
5 3
5 3 2 4 6
107 1 4
*/

后记

也可以使用单调栈求解,回头再思考一下

\(P.\ Contest\ Strategy\)

你可能感兴趣的:($ACM$ 期末考试)