A - Forgetting Things

题意:给 \(a,b\) 两个数字的开头数字(1~9),求使得等式 \(a=b-1\) 成立的一组 \(a,b\) ,无解输出-1。

题解:很显然只有 \(a=b\) 和 \(a=b-1\) 的时候有解,再加上一个从 \(9\) 越到 \(10\) 的就可以了。被样例的 \(199,200\) 误导了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int a, b;
while(~scanf("%d%d", &a, &b)) {
if(a == b)
printf("%d %d\n", a * 10, b * 10 + 1);
else if(a == b - 1)
printf("%d %d\n", a, b);
else if(a == 9 && b == 1)
printf("9 10\n");
else
puts("-1");
}
}

B1 - TV Subscriptions (Easy Version)

见下一题

B2 - TV Subscriptions (Hard Version)

题意:有n天,每天上映一段电视剧的第ai节,要连续看d天,求最少买多少张票。注意买了某一节的票之后就可以反复看这一节。

题解:看数据量,是不是可以二分票的张数,然后尺取暴力验证呢?不过真的t会卡memset,而k的上限又没给,假如想用memset的话要先进行一次离散化。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int n, k, d;
int a[200005];
int cnt[1000005]; bool check(int m) {
int cur = 0;
for(int i = 1; i <= d; ++i) {
++cnt[a[i]];
if(cnt[a[i]] == 1)
++cur;
}
if(cur <= m) {
for(int i = 1; i <= d; ++i)
cnt[a[i]] = 0;
return 1;
}
for(int i = d + 1; i <= n; ++i) {
++cnt[a[i]];
if(cnt[a[i]] == 1)
++cur;
--cnt[a[i - d]];
if(cnt[a[i - d]] == 0) {
--cur;
if(cur <= m) {
for(int j = 0; j < d; ++j)
cnt[a[i - j]] = 0;
return 1;
}
}
}
for(int j = 0; j < d; ++j)
cnt[a[n - j]] = 0;
return 0;
} int bs() {
int l = 1, r = d, m;
while(1) {
m = (l + r) >> 1;
if(l == m) {
if(check(l))
return l;
return r;
}
if(check(m))
r = m;
else
l = m + 1;
}
} int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t;
scanf("%d", &t);
while(t--) {
scanf("%d%d%d", &n, &k, &d);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
printf("%d\n", bs());
}
}

补题题解:其实并不需要二分,扫的时候就知道最小值是谁了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int n, k, d;
int a[200005];
int cnt[1000005]; int solve() {
int cur = 0;
for(int i = 1; i <= d; ++i) {
++cnt[a[i]];
if(cnt[a[i]] == 1)
++cur;
}
int mincur = cur;
for(int i = d + 1; i <= n; ++i) {
++cnt[a[i]];
if(cnt[a[i]] == 1)
++cur;
--cnt[a[i - d]];
if(cnt[a[i - d]] == 0) {
--cur;
if(cur < mincur)
mincur = cur;
}
}
for(int j = 0; j < d; ++j)
cnt[a[n - j]] = 0;
return mincur;
} int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t;
scanf("%d", &t);
while(t--) {
scanf("%d%d%d", &n, &k, &d);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
printf("%d\n", solve());
}
}

C - p-binary

题意:给一个整数n,求它最少用多少个p-binary数组成,无解输出-1,p-binary数是指形如 \(2^x+p\) 的数,其中x非负。

题解:看了下样例,感觉就是不断的尝试减去p(记总共减去了cnt个p),然后判断新的n能不能被cnt个二进制位所表示。不过样例提示我们可以用多个相同的二进制位合成一个高位,所以只需要新的n的二进制位数量不超过cnt就可以了。不过这样做卡了一个数据就是n=101,p=50,这个是输出-1,我这样写输出2,原因是1并不能被2个二进制位表示,因为不能取x=-1,所以得到另一个启发就是k个二进制位能表示的下限就是k(k个1相加),上限是无穷,但是能表示的数必须不超过cnt个1。

为什么比cnt少的二进制位都可以构造呢?因为可以用两个小的组成一个大的来使得cnt变相减少1,而这种方法使用的下限就是cnt个1。

顺带提一下__builtin_popcount()这个函数,其他的不好用但是这个(以及__builtin_ctz()返回后导零的个数)还是不错的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int n, p;
while(~scanf("%d%d", &n, &p)) {
int cnt = 0;
while(cnt < 40) {
n -= p;
if(n <= 0) {
cnt = -1;
break;
}
++cnt;
if(__builtin_popcount(n) <= cnt && n >= cnt)
break;
}
if(cnt == 40)
cnt = -1;
printf("%d\n", cnt);
}
}

D - Power Products

题意:给 \(n\)(2e5)个1e5内的数和 \(k\) (100),求有多少个无序数对 \((i,j)\) 满足存在一个 \(x\) 使得 \(a_ia_j=x^k\) 成立。

题解:

很显然就是每种质因数都要是k的倍数,那么每个数要找的那个数是在模意义下面唯一的,一个暴力的做法是分解1e5内的质数,然后求出不超过1e5的这个质数的最高幂,很明显这个最高幂很快就收敛到只剩下1了,那么可以用一个next指针为[0,16](也就是log(2,1e5))的字典树来存储。这样到后面一般只有两条路可以走。问题在于由于质数过多深度太大。

一种简单的改进就是特判掉k=2的情况,然后质数的范围就真的缩小到sqrt(1e5),超过这个范围的质数最多只有一个,且出现以后不可能会和另一个数配出k>=3时的k的倍数。这样是大概只有65层的一个字典树。

那怎么特判掉k=2的情况呢?应该是存在同一个大质因数的放在一堆,然后堆内匹配。不存在任何大质因数的也是一堆,也是堆内匹配。

补题题解:

本质上是要找唯一配对的一个质因数幂次序列,使用字典树会导致单次修改要经历所有的质数。其实根据唯一分解定理,把这些质因数幂次序列转回去整数存储就可以了,不用搞这么复杂。直接把一个数映射到幂模k的另一个数,然后用map存下来就可以了。甚至不需要用map,他的补数也必须限制在1e5里面的,在找补数的时候注意溢出就可以(使用map的话,应该是可以连溢出都不用管,因为溢出之后大部分应该是找不到答案,应该没有概率说溢出之后绕回来到1e5内)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; const int MAXAI = 1e5;
int cnt[100005]; int p[105], ptop;
bool np[405]; ll qpow(ll x, int n) {
ll res = 1;
while(n) {
if(x > MAXAI)
return -1;
if(n & 1) {
res = res * x;
if(res > MAXAI)
return -1;
}
x = x * x;
//不能在这里判断x溢出,因为有可能这个x不会叠到res上
n >>= 1;
}
return res;
} int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
for(int i = 2, c = sqrt(MAXAI); i <= c; ++i) {
if(np[i])
continue;
p[++ptop] = i;
for(int j = i + i; j <= c; j += i)
np[j] = 1;
} int n, k;
scanf("%d%d", &n, &k);
ll ans = 0;
for(int i = 1, ai; i <= n; ++i) {
scanf("%d", &ai);
ll bi = 1, ci = 1;
for(int j = 1; j <= ptop; ++j) {
if(ai % p[j] == 0) {
int ki = 0;
while(ai % p[j] == 0) {
++ki;
ai /= p[j];
}
ki %= k;
if(ki) {
//假如ki==0,那么k-ki==0,bi和ci都不会变化
ci *= qpow(p[j], ki);
if(bi != -1) {
ll tmp = qpow(p[j], k - ki);
if(tmp != -1) {
bi *= tmp;
if(bi > MAXAI)
bi = -1;
} else
bi = -1;
}
}
}
}
if(ai > 1) {
ci *= ai;
if(bi != -1) {
ll tmp = qpow(ai, k - 1);
if(tmp != -1) {
bi *= tmp;
if(bi > MAXAI)
bi = -1;
} else
bi = -1;
}
}
if(bi != -1)
//当bi不溢出时,看看前面是不是已经统计了这个bi
ans += cnt[bi];
++cnt[ci];
}
printf("%lld\n", ans);
}

注:快速幂里面的溢出不是在x=x*x之后判断,因为有可能会有n>>=1之后n==0,也就是最后一步并不会让res叠上x。

标签:质因数分解

E - Rock Is Push

题意:一个n*m(2000*2000)的棋盘格,每次只能向右或向下走,求走到最右下的不同路径数.注意棋盘格上有石头,可以推动石头,也可以推动一串石头,但是不能把石头推出边界。

补题题解:很显然的一个dp,但是要怎么入手呢?先说一个显然的结论,就是当前行/列的石头只会影响你在这个行/列上前进的终点,在改变方向只会这些石头会再有没有用,所以说每次只需要考虑当前方向上的石头即可。

\(dp[i][j][0]\) 表示从左侧走到格子 \((i,j)\) 的方案数。

\(dp[i][j][1]\) 表示从上侧走到格子 \((i,j)\) 的方案数。

那么怎么转移呢?举个例子,比如现在求的是 \(dp[i][j][1]\) ,从上方进来的话,会有最后一次向右走的位置,记这个位置为 \((k,j)\) ,也就是从左侧进入了 \((k,j)\) ,然后一直往下到 \((i,j)\) ,什么是合法的 \((k,j)\) 呢?当然就是 \((k,j)\) 下方的石头的数量不超过 \((i,j)\) 下方的空格的数量的 \((k,j)\) ,可以看出来随着 \(k\) 不断往上途径的石头会越来越多,这个转折点是确实存在的。一个细节在于这个 \((k,j)\) 这行能不能通过向右走到 \((k,j)\) 是无关紧要的,反正也就加在一起,事实上 \((k,j)\) 应该是上边界某点或者正好在某块石头上(这块石头会被向右推走而不会被向下推,k再上升则会被向下推)

也就是 \(dp[i][j][1]=\sum\limits_{t=k}^{i-1}dp[t][j][0]\)

同理有 \(dp[i][j][0]=\sum\limits_{t=k}^{j-1}dp[i][t][1]\)

方程有了,边界怎么办? \((1,1)\) 格子既可以视作从左侧来也可以视作从上侧来,只是 \(n=m=1\) 的时候要特判掉。

怎么找k呢?以向下转移为例,很显然每个格子下方的石头数是递增的,可以保存每一行每一列的值直接二分,鉴于数据只有2000所以多个11倍常数问题不大。另一种方法是直接从上方的格子转移。

参考资料:

Technocup 2020 — Elimination Round 2 + Codeforces Round 596: analysis - Codeforces

CodeForces 1225E Rock Is Push(dp + 前缀和优化) - alpc_qleonardo - CSDN博客

Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2)的更多相关文章

  1. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) D. Power Products

    链接: https://codeforces.com/contest/1247/problem/D 题意: You are given n positive integers a1,-,an, and ...

  2. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) C. p-binary

    链接: https://codeforces.com/contest/1247/problem/C 题意: Vasya will fancy any number as long as it is a ...

  3. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) B2. TV Subscriptions (Hard Version)

    链接: https://codeforces.com/contest/1247/problem/B2 题意: The only difference between easy and hard ver ...

  4. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) A. Forgetting Things

    链接: https://codeforces.com/contest/1247/problem/A 题意: Kolya is very absent-minded. Today his math te ...

  5. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) F. Tree Factory 构造题

    F. Tree Factory Bytelandian Tree Factory produces trees for all kinds of industrial applications. Yo ...

  6. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) E. Rock Is Push dp

    E. Rock Is Push You are at the top left cell (1,1) of an n×m labyrinth. Your goal is to get to the b ...

  7. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) B. TV Subscriptions 尺取法

    B2. TV Subscriptions (Hard Version) The only difference between easy and hard versions is constraint ...

  8. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) A. Forgetting Things 水题

    A. Forgetting Things Kolya is very absent-minded. Today his math teacher asked him to solve a simple ...

  9. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) D. Power Products 数学 暴力

    D. Power Products You are given n positive integers a1,-,an, and an integer k≥2. Count the number of ...

随机推荐

  1. csv注入复现代码

    以下代码生成的csv文件,使用Microsoft Execl能成功弹出计算器,虽然打开时有安全提示,但是大多数src还是会接收该类漏洞 -------------------------------- ...

  2. oracle 01741:非法的零长度标识

    转自:https://blog.csdn.net/wanderball/article/details/7690206 出现此问题是标识符里有两个连续的“”号,去掉即可,或是里面填充内容,避免两个连续 ...

  3. LCD驱动的学习

    简介: LCD是基于液晶的. LCD(liquid crystal display)按驱动方式分类可以分为静态驱动,简单矩阵驱动,主动矩阵驱动.其中,简单矩阵又可以分为扭转向列型(TN)和超转向列型( ...

  4. java包装类的自动装箱及缓存

    首先看下面一段代码 public static void main(String[] args) { Integer a=1; Integer b=2; Integer c=3; Integer d= ...

  5. HBase分布式搭建常见错误

    [root@node001 bin]# hbase shell SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found bin ...

  6. sql sever2008 R2 检测到索引可能已损坏。请运行 DBCC CHECKDB。

    1.设置成单用户状态 USE MASTER ALTER DATABASE DBNAME SET SINGLE_USER; GO --DBNAME为修复的数据库名 2.执行修复语句,检查和修复数据库及索 ...

  7. Android笔记(十六) 简易计算器

    实现功能: 简单计算器 布局及美化 采用LinearLayout嵌套LinearLayout实现布局. 要求 1. 按钮所有文字居于右下角 2. 按钮为白色,点击变成橘色 3. 显示屏文字居右显示并且 ...

  8. KVM虚拟化——简介

    KVM 基于内核的虚拟机KVM(Kernel-Based Virtual Machine)是2007年问世的开源虚拟化解决方案.KVM需要两个条件: ①硬件支持全虚拟化 ②操作系统为Linux KVM ...

  9. 动态规划——python

    1.爬楼梯问题一个人爬楼梯,每次只能爬1个或两个台阶,假设有n个台阶,那么这个人有多少种不同的爬楼梯方法 动态规划的状态转移:第 i 个状态的方案数和第 i-1, i-2时候的状态有关,即:dp[i] ...

  10. 某网站的videojs的配置及操作

    某网站的videojs的配置及操作 一.总结 一句话总结: 多参照参照别人的例子就好,省事 1.videojs如何获取用户当前视频的位置? this.currentTime() 2.回到视频开始处? ...