【杂题合集】震惊!这种生活习惯可能致癌!99%的人都有......

杂题合集

基本上都是不太难的思维题,难的我也写不出来。
标题党去死。

CF1714E Add Modulo 10

题意概述:

给出一个序列,可以给其中任意一个数加上当前它的个位数,问进行若干次操作后这个序列里的数能否全部相等。

解析:

思维题,不算难。

观察个位数,找规律:

  • 个位数是 \(0\):只能是 \(0\)。
  • 个位数是 \(1\) :\(1 → 2\)。
  • 个位数是 \(2\) :\(2 → 4 → 8 → 6 → 2\)。
  • 个位数是 \(3\) :\(3 → 6 → 2\)。
  • 个位数是 \(4\):\(4 → 8 → 6 → 2\)。
  • 个位数是 \(5\):\(5 → 0\)。
  • 个位数是 \(6\):\(6 → 2\)。
  • 个位数是 \(7\):\(7 → 4 → 8 → 6 →2\)。
  • 个位数是 \(8\):\(8 → 6 → 2\)。
  • 个位数是 \(9\):\(9 → 8 → 6 →2\)。

可见,除个位数为 \(0\) 和 \(5\) 外,所有的数都可将个位数变为 \(2\)。

对于个位数是 \(5\) 的数,就给它加上 \(5\),此时只有其他数都与它相等,序列才合法。

对于其他数,都把它变成以 \(2\) 为个位数的数。注意到个位数从 \(2\) 再变到 \(2\),相当于加了 \(20\)。
将变化后的数列sort一遍,判断相邻的两数之差是不是 \(20\) 的倍数就行了。

Code

点击查看代码
#include<cstdio>
#include<algorithm>

using namespace std;

const int MAXN = 2e5 + 10;
int t, n;
int num[MAXN];

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

bool Check(){
    sort(num + 1, num + 1 + n);
    if(num[1] % 10 == 0){
        if(num[n] == num[1]) return true;
        else return false;
    }

    for(register int i = 2; i <= n; i++){
        int cut = num[i] - num[i - 1];
        if(cut % 20) return false;
    }

    return true;
}

int main(){
    t = read();
    while(t--){
        n = read();
        for(register int i = 1; i <= n; i++){
            num[i] = read();

            if(num[i] % 10 != 2){
                switch(num[i] % 10){
                    case 1 : num[i] += 1; break;
                    case 3 : num[i] += 9; break;
                    case 4 : num[i] += 18; break;
                    case 5 : num[i] += 5; break;
                    case 6 : num[i] += 6; break;
                    case 7 : num[i] += 25; break;
                    case 8 : num[i] += 14; break;
                    case 9 : num[i] += 23; break;
                }
            }
        } 

        if(Check()) puts("Yes");
        else puts("No");
    }

    return 0;
}

CF1711B Party

题目概述:

给出一个 \(n\) 个点,\(m\) 条边的无向图,问删去多少的点使得删去的点权最小,且留下的点组成的图中有偶数条边。输出删去的点的点权和。

解析:

如果 \(m\) 为偶数,不用删点。

如果 \(m\) 为奇数,删点的同时删掉的边要为奇数条,大力分类讨论:

  1. 删一个点:显然要选择一个度数为奇数的点。
  2. 删两个点:再度大力分类讨论:
    1. 删去两个相连的,且度数都为偶数的点。
    2. 删去两个相连的,且度数都为奇数的点。
    3. 删去不相连的,度数一奇一偶的点:
      但是我们可以只删去那个度数为奇数的点,这样做会更优,所以这种情况一定不会有最优解。
  3. 删三个点:先考虑删去一个度数为奇,两个度数为偶且两两不相连的点。但这样完全可以只删去那个度数为奇的点,所以删去三个点一定不会有最优解。

所以,最优解只有可能在三种情况中出现,即删掉一个度数为奇的点、删掉有相连的两个度数都为奇的点、删掉相连的两个度数都为偶数的点。

因为 \(n,m ≤ 10 ^ {5}\),删去一个点的直接枚举点,删去两个点的直接枚举边即可。

Code

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 1e5 + 10, MAXM = 1e5 + 10;
int t, n, m, ans;
int unhap[MAXN];
int from[MAXM], to[MAXM], deg[MAXN];

inline void Clear(){
    ans = 2147483647;
    memset(deg, 0, sizeof(deg));
}

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    t = read();
    while(t--){
        Clear();
        n = read(), m = read();
        for(register int i = 1; i <= n; i++)
            unhap[i] = read();
        for(register int i = 1; i <= m; i++){
            int u, v;
            u = read(), v = read();
            deg[u]++, deg[v]++;
            from[i] = u, to[i] = v;
        }

        if(!(m & 1)){ 
            puts("0");
            continue;
        }

        for(register int i = 1; i <= n; i++)
            if(deg[i] & 1) ans = min(ans, unhap[i]);
        for(register int i = 1; i <= m; i++){
            int u = from[i], v = to[i];
            if(!((deg[u] + deg[v]) & 1))
                ans = min(ans, unhap[u] + unhap[v]);
        }

        printf("%d\n", ans);
    }

    return 0;
}

CF1605C Dominant Character

题目概述:

给定一个由 abc 组成的字符串,找出一个最短的子串,满足 a 的数量大于 b 的数量和 c 的数量。

解析:

大力讨论:

  1. 串长为 \(2\),两个 a 相邻。
  2. 串长为 \(3\),所以仅当两个 a 中间隔了一个 b 或 c,即 abc 和 aca。
  3. 串长为 \(4\),a 的数量至少是 \(2\), a 不能相邻,只能是 abca 或 acba,不会有 \(3\) 个 a的情况。
  4. 串长为 \(5\),a 的数量至少是 \(3\),则会有长为 \(3\) 的子串中有两个 a,不成立。
  5. 串长为 \(6\),a 的数量至少是 \(3\),会出现长为 \(3\) 的子串中有两个 a,不成立。
  6. 串长为 \(7\),a 的数量至少是 \(4\),且a 之间需要有三个不是 a 的字母隔开。

之后以此类推,发现答案只可能到 \(7\),再长的子串都能都被拆分成更短的满足要求的子串,之后枚举即可。

点击查看代码

Code

#include<cstdio>

using namespace std;

const int MAXN = 1e6 + 10;
int t, n;
char s[MAXN];

int main(){
    scanf("%d", &t);
    while(t--){
        bool flag = false;
        scanf("%d", &n);
        scanf("%s", s + 1);

        for(register int i = 2; i <= n && !flag; i++){
            if(s[i] == 'a' && s[i - 1] == 'a'){
                puts("2");
                flag = true;
            }
        }
        if(n >= 3)
            for(register int i = 2; i <= n - 1 && !flag; i++){
                if(s[i - 1] == 'a' && s[i + 1] == 'a'){
                    puts("3");
                    flag = true;
                }
            }
        if(n >= 4)
            for(register int i = 2; i <= n - 2 && !flag; i++){
                if(s[i - 1] == 'a' && s[i + 2] == 'a' && ((s[i] == 'b' && s[i + 1] == 'c') || (s[i] == 'c' && s[i + 1] == 'b'))){
                    puts("4");
                    flag = true;
                }
            }
        if(n >= 7)
            for(register int i = 4; i <= n - 3 && !flag; i++){
                if(s[i] == 'a' && s[i - 3] == 'a' && s[i + 3] == 'a'){
                    if(s[i - 1] == 'b' && s[i - 2] == 'b' && s[i + 1] == 'c' && s[i + 2] == 'c'){
                        puts("7");
                        flag = true;
                    }
                    else if(s[i - 1] == 'c' && s[i - 2] == 'c' && s[i + 1] == 'b' && s[i + 2] == 'b'){
                        puts("7");
                        flag = true;
                    }
                }
            }
        
        if(!flag) puts("-1");
    }

    return 0;
}

CF1714G Path Prefixe

题目概述:

给你一颗以一号节点为根的树,每个节点上有两个权值,分别为 \(a_{i}\) 和 \(b_{i}\)。记 \(A_{i}\) 为从根到节点 \(i\) 的 \(a_{i}\) 的前缀和,求最长的使得 \(b_{i}\) 的和小于等于 \(A_{i}\) 的前缀。

解析:

其实题目概述写啰嗦了,我的确概述不了这道题。读懂了题就很简单了。

看数据范围 \(n ≤ 2 \times 10 ^ {5}\),以及题目中的最长、不大于,可以往二分上靠。

树论的话 dfs 是必不可少的,用 dfs 求出每个节点 \(a\) 和 \(b\) 的前缀和,把当前节点的 \(b\) 的前缀和压进栈里,本层的 dfs 结束时再把它弹出栈,就可以保证栈里的元素全部是从根节点到当前节点路径上,每个点的 \(b\) 的前缀和,upper_bound 一下即可求出最长的前缀。

注意开 long long,CF Div.3 光卡 long long。

Code

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>

#define LL long long

using namespace std;

const int MAXN = 2e5 + 10;
int t, n, cnt, top;
int head[MAXN], ans[MAXN];
LL sum_a[MAXN], sum_b[MAXN]; 
LL stk[MAXN];

struct Edge{
    int to, next, dis_a, dis_b;
}e[MAXN << 1];

inline void Add(int u, int v, int a, int b){
    e[++cnt].to = v;
    e[cnt].dis_a = a;
    e[cnt].dis_b = b;
    e[cnt].next = head[u];
    head[u] = cnt;
}

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

void dfs(int rt, int fa){
    stk[++top] = sum_b[rt];

    for(register int i = head[rt]; i; i = e[i].next){
        int v = e[i].to;
        if(v == fa) continue;
        sum_a[v] = sum_a[rt] + e[i].dis_a;
        sum_b[v] = sum_b[rt] + e[i].dis_b;
        dfs(v, rt);
    }

    int pos = upper_bound(stk + 1, stk + 1 + top, sum_a[rt]) - stk;
    ans[rt] = pos - 2;
    top--;
}

void Clear(){
    cnt = 0;
    top = 0;
    memset(head, 0, sizeof(head));
}

int main(){
    t = read();
    while(t--){
        Clear();

        n = read();
        for(register int i = 2; i <= n; i++){
            int p, a, b;
            p = read(), a = read(), b = read();
            Add(p, i, a, b);
            Add(i, p, a, b);
        }

        dfs(1, 0);

        for(register int i = 2; i <= n; i++)
            printf("%d ", ans[i]);
        puts("");
    }

    return 0;
}

CF1714F Build a Tree and That Is It

题目概述:

概不出来了自己品吧。

树是一个没有环的无向连通图,注意,在本题中,我们讨论的是无根树

现有四个整数 \(n, d_{12}, d_{23}\) 和 \(d_{31}\),构建一颗满足以下条件的树:

  • 包含从 \(1\) 到 \(n\) 的 \(n\) 个节点;
  • 从节点 \(1\) 到节点 \(2\) 的距离(最短路的长度)为 \(d_{12}\);
  • 从节点 \(2\) 到节点 \(3\) 的距离为 \(d_{23}\);
  • 从节点 \(3\) 到节点 \(1\) 的距离为 \(d_{31}\)。

输出满足条件的任意一棵树;若不存在,输出 NO。

解析:

每条边的边权为 \(1\)。

题目说是无根树,可以先假设它有一个根节点,设 \(d_{1}\),\(d_{2}\),\(d_{3}\) 三条互不相交的分别连接根节点和 \(1\),\(2\),\(3\) 号节点的链的长度。

然后就可得到:

\[\left\{\begin{matrix} d_{12} = d_{1} + d_{2} \\ d_{23} = d_{2} + d_{3} \\ d_{31} = d_{1} + d_{3} \end{matrix}\right. \]

只后我们移项,消元:

\[\left\{\begin{matrix} d_{1} = \frac{d_{12} + d_{31} - d_{23}}{2} \\ d_{2} = \frac{d_{12} + d_{23} - d_{31}}{2} \\ d_{3} = \frac{d_{23} + d_{31} - d_{12}}{2} \end{matrix}\right. \]

其中 \(d_{1}\),\(d_{2}\),\(d_{3}\) 可能为 \(0\),代表该节点就是根节点。

至于无解的情况,显然,\(d_{1}\),\(d_{2}\),\(d_{3}\) 都要 \(≥0\),且\(d_{12} + d_{31} - d_{23}\),\(d_{12} + d_{23} - d_{31}\),\(d_{23} + d_{31} - d_{12}\) 都应为偶数。

构造完这 \(3\) 条链后的树即为满足 \(3\) 个点之间的约束条件的最小的树,判断总的节点数 \(tot = d_{1} + d{2} + d{3} + 1\) (从根节点到 \(1\),\(2\),\(3\) 号节点的链的长度就是链上的节点数,最后要加上根节点)与 \(n\) 的大小关系:

  • \(tot < n\),则剩余的节点在任意节点上都不会影响树的合法性。
  • \(tot = n\),构造结束。
  • \(tot > n\),不合法,构造失败。

之后我们需要找出根节点是谁,若 \(d_{1}\),\(d_{2}\),\(d_{3}\) 都不为 \(0\),我们可以让任意一个 \(>3\) 且 \(≤n\) 的节点为根节点,这里选择了 \(4\) 号节点。

最后剩下的节点随便连谁都可以,这里选择接在 \(3\) 号节点。

Code

点击查看代码
#include<cstdio>

using namespace std;

int t, n, d12, d23, d31;
int root, tot, d[4];

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

void Build_Tree(int x){
    printf("%d ", root);
    for(register int i = 1; i < d[x]; i++){
        ++tot;
        printf("%d\n", tot);
        printf("%d ", tot);
    }
    printf("%d\n", x);
}

void Clear(){
    tot = 0;
    root = 0;
    d[1] = d[2] = d[3] = 0;
}

int main(){
    t = read();
    while(t--){
        Clear();

        n = read();
        d12 = read(), d23 = read(), d31 = read();

        d[1] = (d12 + d31 - d23) / 2;
        d[2] = (d12 + d23 - d31) / 2;
        d[3] = (d23 + d31 - d12) / 2;

        if(d[1] + d[2] + d[3] + 1 > n || d[1] < 0 || d[2] < 0 || d[3] < 0){
            puts("NO");
            continue;
        }
        if((d12 + d31 - d23) & 1 || (d12 + d23 - d31) & 1 || (d23 + d31 - d12) & 1){
            puts("NO");
            continue;
        }

        puts("YES");

        tot = 3;
        if(!d[1]) root = 1;
        if(!d[2]) root = 2;
        if(!d[3]) root = 3;
        if(!root){
            tot = 4;
            root = 4;
        }

        if(1 != root) Build_Tree(1);
        if(2 != root) Build_Tree(2);
        if(3 != root) Build_Tree(3);

        while(tot < n) printf("3 %d\n", ++tot);
    }

    return 0;
}

P7913 [CSP-S 2021] 廊桥分配

没考试,刷点历年原题。看着它是21年的T1,应该好欺负,然后想了一中午才想出来。

题目概述:

概NM,自己看。

解析:

廊桥先到先得,仅于飞机的起降顺序有关,加入第 \(i\) 条廊桥不会对前 \(i - 1\) 条廊桥的分配产生影响。我们设 \(f(x)\) 代表分配 \(x\) 条廊桥能停靠的国内航班数,\(g(x)\) 代表分配 \(x\) 条廊桥能停靠的国际航班数,可得 \(f(x) = f(x - 1) + 再加入一个廊桥产生的贡献\),\(g(x) = g(x - 1) + 再加入一个廊桥产生的贡献\)。

考虑每加入一条廊桥会影响哪些飞机。用 set 来维护当前在远机位和未降落的飞机。每加入一条廊桥,当前 set 顶端的飞机 \(p\) 停靠到廊桥,之后,set 中在 \(p\) 起飞后第一个降落的飞机 \(p'\) 停靠到廊桥......以此类推。

直接在 set 中二分查找,记录次数,然后再把找到的飞机清出 set(停靠过一遍廊桥即起飞),最后加入这条廊桥的贡献就是找到的飞机的次数。

Code

点击查看代码
#include<cstdio>
#include<algorithm>

using namespace std;

const int MAXN = 1e5 + 10;
int n, m1, m2, ans;
int sum1[MAXN], sum2[MAXN];

struct Plane{
    int st, ed;

    bool operator < (const Plane &a) const{
        return st < a.st;
    }
}p1[MAXN], p2[MAXN];

multiset<Plane> s1, s2;

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int Get_Num(multiset<Plane> &s){
    int ans = 0;
    auto pos = s.begin();

    if(pos == s.end()) return 0;

    Plane p = *pos;
    p.st = p.ed;
    ++ans;
    s.erase(pos);

    while(!s.empty()){
        pos = s.lower_bound(p);
        if(pos == s.end()) break;
        ++ans;
        p = *pos;
        p.st = p.ed;
        s.erase(pos);
    }

    return ans;
}

int main(){
    n = read(), m1 = read(), m2 = read();
    for(register int i = 1; i <= m1; i++){
        p1[i].st = read(), p1[i].ed = read(); //国内航班
        s1.insert(p1[i]);
    }
    for(register int i = 1; i <= m2; i++){
        p2[i].st = read(), p2[i].ed = read(); //国外航班
        s2.insert(p2[i]);
    }

    sort(p1 + 1, p1 + 1 + m1);
    sort(p2 + 1, p2 + 1 + m2);

    for(register int i = 1; i <= n; i++)
        sum1[i] = sum1[i - 1] + Get_Num(s1);
    for(register int i = 1; i <= n; i++)
        sum2[i] = sum2[i - 1] + Get_Num(s2);

    for(register int i = 0; i <= n; i++)
        ans = max(ans, sum1[i] + sum2[n - i]);

    printf("%d", ans);

    return 0;
}

P2515 [HAOI2010]软件安装

边写边颓,结果一A了(坏了,我成wonder了)。

题目概述:

自己概去,语文功底不好,概不出来。

解析:

最开始当图论题打的,结果越打越不对,发现是个树上的背包问题。

首先,每个软件之间有依赖关系,即软件 \(i\) 只有在安装了软件 \(D_i\) 之后才能正常工作,考虑从 \(D_i\) 向 \(i\) 连边,最后会得到一个类似树形的关系。然后就理所应当的想到拓扑排序或者树形DP了是吧。

问题在于这个关系并不能直接构成树,每个关系(每条边)都是单向的,所以可能出现游离在外的点或环,比如 \(D_1 = 2, D_2 = 3, D_3 = 1\),这时 \(1, 2, 3\) 就形成了一个独立的环。这个环要么都选,要么都不选,所以可以用tarjan缩点,把环缩成一个联通块。

缩完点之后仍然有独立的点,需要把它变成一个树状结构。考虑将所有入度为 \(0\) 的点和 \(0\) 连一条边,这样这张图就可以转化成一棵以 \(0\) 为根的树,然后就可以在树上跑01背包了。

设定状态 \(dp[i][j]\) 代表以 \(i\) 为根的子树在 \(j\) 的容量下能得到的最大的价值,在递归到当前节点的时候记得初始化一下DP数组就行了。为啥不讲转移?就是个普通的01背包,讲啥啊。

Code

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 110, MAXM = 510;
int n, m, cnt, num, tot;
int head[MAXN], spa[MAXN], val[MAXN], from[MAXN], to[MAXN];
int dfn[MAXN], low[MAXN], belong[MAXN], room[MAXN], value[MAXN], in[MAXN];
int stk[MAXN], top;
int dp[MAXN][MAXM];
bool vis[MAXN];

struct Edge{
    int to, next;
}e[MAXN << 1];

inline void Add(int u, int v){
    e[++cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

void Tarjan(int u){
    dfn[u] = low[u] = ++num;
    stk[++top] = u;
    vis[u] = true;

    for(register int i = head[u]; i; i = e[i].next){
        int v = e[i].to;
        if(!dfn[v]){
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(vis[v]) low[u] = min(low[u], dfn[v]);
    }

    if(low[u] == dfn[u]){
        ++tot;
        int t;
        do{
            t = stk[top--];
            vis[t] = false;
            belong[t] = tot;
            room[tot] += spa[t];
            value[tot] += val[t];
        }while(t != u);
    }
}

void Rebuild(){
    cnt = num = 0;
    memset(head, 0, sizeof(head));

    for(register int i = 1; i <= n; i++){
        int u = from[i], v = to[i];
        int blu = belong[u], blv = belong[v];

        if(!u) continue;
        if(blu != blv){
            Add(blu, blv);
            ++in[blv];
        }
    }

    for(register int i = 1; i <= tot; i++)
        if(!in[i]) Add(0, i);
}

void dfs(int rt){
    for(register int i = room[rt]; i <= m; i++)
        dp[rt][i] = value[rt];

    for(register int i = head[rt]; i; i = e[i].next){
        int v = e[i].to;
        dfs(v);

        int k = m - room[rt];
        for(register int i = k; i >= 0; i--)
            for(register int j = 0; j <= i; j++)
                dp[rt][i + room[rt]] = max(dp[rt][i + room[rt]], dp[v][j] + dp[rt][i + room[rt] - j]);
    }
}

int main(){
    n = read(), m = read();
    for(register int i = 1; i <= n; i++)
        spa[i] = read();
    for(register int i = 1; i <= n; i++)
        val[i] = read();
    for(register int i = 1; i <= n; i++){
        int u;
        u = read();
        if(u) Add(u, i);
        from[i] = u, to[i] = i;
    }

    for(register int i = 1; i <= n; i++)
        if(!dfn[i]) Tarjan(i);
    
    Rebuild();

    dfs(0);

    printf("%d\n", dp[0][m]);

    return 0;
}
posted @ 2022-09-15 16:15  TSTYFST  阅读(93)  评论(0)    收藏  举报