CF刷题

rainbowのCF 计划

从21号开始打算就不都写在这篇文章里面了,分开写

每天一篇博客,在CF专栏里面

欢迎私信催更

–不能总是很差很差的,我讨厌我现在的自己

前言:

​ 我总是这样,有这样那样的计划,却不去执行;

​ 看吧看吧,我总是最差的(这不是我想要的,

​ 弄的这么累干什么呢

​ 我暂且每天定一个计划,写两道题目or一道难题,再加更新题解

​ 发在CDSN上,望自己能够监督自己

​ 任何人都靠不住,我说是任何人,自己才能够提醒自己

​ 什么都抓不住的话,那就死掉好了!

4.14

k-Tree / DP / 1600

C. k-Tree

题意

给出K-Tree定义,每个结点都有恰好K个孩子,这棵树无限增长。每个节点到它K个孩子的K条边的权重刚好是1,2,3…,K。

现在问有多少条路径,使得从根节点出发到达某个结点,经过的边权重之和恰好为n,并且经过的边至少有一条权重不小于d。

题解

一个维度表示权值j和为i

用另一个维度表示到该边为止是否已经出现过权重不小于d的边。

dp[i][0]:表示权值和为i中不包含权值>=d的边。
dp[i][j]:表示权值和为i中包含权值>=d的边。

dp[i][0]+=dp[i-j][0] (j<d)

dp[i][1]+=dp[i-j][0] (j>=d)

dp[i][1]+=dp[i-j][1];

D P 重 点 还 是 分 析 最 终 态 ! \color{red}{DP重点还是分析最终态!} DP

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int MAX = 1e3 + 10;

int dp[MAX][2];

int main() {
    int n, k, d;
    scanf("%d%d%d", &n, &k, &d);
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= min(k, n); j++) {
            if(j < d && i - j >= 0) {
                dp[i][0] += dp[i - j][0];
                dp[i][1] += dp[i - j][1];
            }
            if(j >= d && i - j >= 0) {
                dp[i][1] += dp[i - j][0];
                dp[i][1] %= MOD;
                dp[i][1] += dp[i - j][1];
            }
            dp[i][0] %= MOD;
            dp[i][1] %= MOD;
        }
    }
    printf("%d\n", dp[n][1]);
}

Spreadsheets / 模拟 / 1600

Spreadsheets

题意

Excel是最常用的办公软件。每个单元格都有唯一的地址表示。比如:第12行第4列表示为:“D12”,第5行第255列表示为“IU5”。
事实上,Excel提供了两种地址表示方法,还有一种表示法叫做RC格式地址。
第12行第4列表示为:“R12C4”,第5行第255列表示为“R5C255”。
你的任务是:编写程序,实现从RC地址格式到常规地址格式的转换。
【输入、输出格式要求】
用户先输入一个整数n(n<100),表示接下来有n行输入数据。
接着输入的n行数据是RC格式的Excel单元格地址表示法。
程序则输出n行数据,每行是转换后的常规地址表示法。

s s c a n f ( a , " % [ A − Z ] % d " , c , & n 2 ) ; / / s s c a n f ( a , % [ A − Z ] , c ) ; 把 大 写 的 字 母 都 吸 收 \color{red} sscanf(a, "\%[A-Z]\%d", c, \&n2);//sscanf(a,\%[A-Z],c);把大写的字母都吸收 sscanf(a,"%[AZ]%d",c,&n2);//sscanf(a,%[AZ],c);

s c a n f 可 以 从 c h a r 读 入 \color{red}scanf可以从char读入 scanfchar

#include<bits/stdc++.h>

using namespace std;

int judge(char a[]) {
    int len = strlen(a);
    if (a[0] == 'R') {
        for (int i = 0; i < len; i++) {
            if (isdigit(a[i]) && i >= 1)
                continue;
            if (a[i] == 'C' && isdigit(a[i - 1]) && isdigit(a[i + 1]))
                return 1;
        }
    }
    return 0;
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n1, n2;
        char a[200];
        int b[200] = {0};
        char c[200] = {'\0'};
        scanf("%s", a);
        if (judge(a)) {
            sscanf(a, "R%dC%d", &n1, &n2);//a里面输入n1 和 n2
            int i = 0;
            while (n2 > 0) {
                if (n2 % 26 == 0) {//和一般的mod不一样
                    b[i++] = 26;
                    n2--;
                } else {
                    b[i++] = n2 % 26;
                }
                n2 /= 26;
            }
            for (i--; i >= 0; i--) {
                printf("%c", b[i] + 'A' - 1);
            }
            printf("%d\n", n1);
        }
        else {
            sscanf(a, "%[A-Z]%d", c, &n2);//sscanf(a,%[A-Z],c);把大写的字母都吸收
            int len = strlen(c);
            reverse(c, c + len);
            n1 = 0;
            for (int i = 0; i < strlen(c); i++) {
                n1 += ((c[i] - 'A' + 1) * pow(26, i));
            }
            printf("R%dC%d\n", n2, n1);
        }
    }
    return 0;
}

4.15

Celex Update / 思维 / 1600

Celex Update

题意

给你一个这样的图形,只能向右向左走,问你从(x1,y1)走到(x2,y2),对经过的数字进行求和,问你求得的和有多少种

题解

同一种颜色的块,从右上到左下,大小一次增加1,比如我们要从(1,1)走到(3,3),最小的和是1-2-4-8-13,如果我们想在这个和的基础上+1,只要走4的左下那个格子,也就是5,其余的保持不变即可。

那么我们很容易猜想到,所有总和就是最小到最大之间的所有数。

思 维 方 式 : 既 然 说 数 字 和 不 一 样 , 那 就 考 虑 最 小 数 字 是 多 少 , 看 递 增 的 规 律 \color{red}思维方式:既然说数字和不一样,那就考虑最小数字是多少,看递增的规律

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        ll x1, y1, x2 ,y2;
        scanf("%lld %lld %lld %lld", &x1, &y1, &x2, &y2);
        ll ans = (y2 - y1) * (x2 - x1) + 1;
        printf("%lld\n", ans);
    }
}

Good Subarrays / 思维 / 1600

Good Subarrays

题意

求n长度数组中区间和和为区间长度的区间个数。

题解

一个优化,区间和减去1,区间和对于长度变成区间和等于0,那么,就简单好多

如果我们将数组中所有元素都-1,那么问题就简单了不少,符合条件的区间和为0。问题转换为求区间和为0的区间个数,就可以开始整活了。

众所周知,和为0的区间,减去和为0的区间,就可以找到一个和为0的新区间。(类似于集合运算的A-B)
同理可得,和为x的区间,减去区间和为x的区间,也是一个和为0的新区间。
综上所述,我们只需遍历一遍前缀和sum[i],如果sum[i]为0,ans++。再加上 前i-1个元素前缀和为当前sum[i]的个数,就是最终答案

什 么 什 么 前 缀 和 的 一 定 要 警 惕 负 数 , 尽 量 用 m a p \color{red}什么什么前缀和的一定要警惕负数,尽量用map map

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 10;

int a[MAX];
int sum[MAX];

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        memset(a, 0, sizeof a);
        memset(sum, 0, sizeof sum);
        map<int, int> mp;//一定要用mp,因为sum会变成负数
        string ch;
        int n;
        scanf("%d", &n);
        cin >> ch;
        for(int i = 1; i <= n; i++) {
            a[i] = ch[i - 1] - '0' - 1;
            sum[i] = a[i] + sum[i - 1];
        }
        ll ans = 0;
        for(int i = 1; i <= n; i++) {
            if(sum[i] == 0) ans++;
            ans += mp[sum[i]];
            mp[sum[i]]++;
        }
        printf("%lld\n", ans);
    }
}

4.16

4.16准备省赛,请假

4.17 省赛

小结:银牌中游,预期左右叭

不说队内打的怎么样,就说自己叭

感觉自己没什么用,我说了我是弱化版的whm,思维题目比不过人家,最多比他会读题

(幸运的是最近两场比赛都是思维场,要是有难题的板子题,我们瞬间爆炸

我的贡献就是在前4道签到题目的时候,统筹兼顾,而且出了一道题,后面有一题给了思路,然后负贡献是给了一个假算法

亏的我还是队长,竟然这么没用,看看沈阳站也去不了,因为我的负反馈。

我要狂补数据结构,那些算法一定要理解,一定要会用

多校打起来,队友迫害起来

4.18, 4.19

//忙学业

//只做了一题

Number of Ways / 思维 / 1700

题意

给一个数组,让其分成三个连续的部分,3个部分的和相等,问有多少种分配方法?

解法

要平均分配成3个部分,肯定总和是3的倍数,然后记录一下其前缀和上x,2x,3x的个数。从前往后遍历,如果遇到了2x,就看前面有多少个x,加到结果中即可。

注 意 要 注 意 两 个 点 除 了 不 可 能 是 第 一 和 最 后 一 个 点 , 而 且 有 先 后 顺 序 , 不 能 直 接 的 乘 \color{red}注意要注意两个点除了不可能是第一和最后一个点,而且有先后顺序,不能直接的乘

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e6 + 10;
ll a[MAX];
ll sum[MAX];

int main() {
    int n;
    scanf("%d", &n);
    ll tot = 0;
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        tot += a[i];
        sum[i] = sum[i - 1] + a[i];
    }
    if(tot % 3 != 0) {
        printf("0\n");
        return 0;
    }
    tot /= 3;
    ll ans = 0;
    int cnt = 0;
    for(int i = 1; i <= n - 1; i++) {
    	if(sum[i] == tot * 2) ans += cnt;
    	if(sum[i] == tot) cnt++;
	}
    printf("%lld\n", ans);
}

4.20 Unmerge /思维 / 1800

Unmerge

题意

给定一共长度为 $ 2 × n $ 的数组(每个数都不相同)。问能不能将其划分成两个长度为 n nn 的数组。满足:每次取这两个数组开头的较小的数放入队列,直至取完,队列中的数列与给出的$ 2 × n $的数组相同。

题解

如果一个数后面有一串比他小的数,那么这一段一定来自同一个序列

比如5 3 1 6 4 2

可 以 分 组 为 [ 5 , 3 , 1 ] , [ 6 , 4 , 2 ] 可以分组为[5,3,1],[6,4,2]可以分组为[5,3,1],[6,4,2]

为 啥 5 , 3 , 1 一 定 在 同 一 组 内 ? 为啥5,3,1一定在同一组内?为啥5,3,1一定在同一组内?

因 为 这 三 个 数 能 拿 出 来 , 一 定 是 另 一 组 的 某 个 数 字 比 5 还 大 , 限 制 了 这 些 数 因为这三个数能拿出来,一定是另一组的某个数字比5还大,限制了这些数因为这三个数能拿出来,一定是另一组的某个数字比5还大,限制了这些数

所以问题转换为分成一些组,要求能凑出n个数的序列

01背包乱转移去吧

要 写 初 始 化 一 定 要 看 \color{red}要写初始化一定要看

01 背 包 的 话 , 二 维 初 始 化 第 一 个 就 好 了 ( 如 果 空 间 允 许 的 话 ) , 一 维 优 化 的 话 , 就 要 优 化 所 有 的 背 包 \color{red}01背包的话,二维初始化第一个就好了(如果空间允许的话),一维优化的话,就要优化所有的背包 01

#include <bits/stdc++.h>
using namespace std;
const int MAX = 4e3 + 5;
int a[MAX];
int cnt[MAX];
int dp[MAX];
int w[MAX];
int main() {
	int T;
	scanf("%d", &T);
	while(T--) {
		int n;
		scanf("%d", &n);
		for(int i = 1; i <= 2 * n; i++) {
			scanf("%d", &a[i]);
			cnt[i] = 0;
		}
		int j = a[1];
		for(int i = 1; i <= 2 * n; i++) {
			if(a[i] >= j) {
				cnt[i] = 1;
				j = a[i];
			}
		}
		int cntt = 0;
		vector<int> cc;
		for(int i = 1; i <= 2 * n + 1; i++) {
			if(cnt[i] != 0 || i == 2 * n + 1) {
				cc.push_back(cntt);
				cntt = 1;
			}
			else {
				cntt++;
			}
		}
		memset(dp, 0, sizeof (int) * (n + 1));
		//dp[n] = 0;
		int len = 1;
		for(auto x: cc) {
			w[len++] = x;
		}
		for(int i = 1; i < len; i++)  {
			for(int j = n; j >= w[i]; j--) {
			    dp[j] = max(dp[j - w[i]] + w[i], dp[j]);
			}
		}
		if(dp[n] == n)
			printf("YES\n");
		else {
			printf("NO\n");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值