rainbowのCF 计划
从21号开始打算就不都写在这篇文章里面了,分开写
每天一篇博客,在CF专栏里面
欢迎私信催更
–不能总是很差很差的,我讨厌我现在的自己
前言:
我总是这样,有这样那样的计划,却不去执行;
看吧看吧,我总是最差的(这不是我想要的,
弄的这么累干什么呢
我暂且每天定一个计划,写两道题目or一道难题,再加更新题解
发在CDSN上,望自己能够监督自己
任何人都靠不住,我说是任何人,自己才能够提醒自己
什么都抓不住的话,那就死掉好了!
4.14
k-Tree / DP / 1600
题意
给出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
题意
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,"%[A−Z]%d",c,&n2);//sscanf(a,%[A−Z],c);把大写的字母都吸收
s c a n f 可 以 从 c h a r 读 入 \color{red}scanf可以从char读入 scanf可以从char读入
#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
题意
给你一个这样的图形,只能向右向左走,问你从(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
题意
求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
题意
给定一共长度为 $ 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");
}
}
}