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. WebApi 身份认 Basic基础认证

    <body> <div style="text-align:center;"> <div>用户名:<input type="te ...

  2. 90% 的 Python 开发者不知道的描述符应用

    经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的. 正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Py ...

  3. jQuery标签操作

    样式操作 样式类操作 //添加指定的css类名 $('元素选择器')addClass('类名'); //移除指定的css类名 removeClass(); //判断样式存不存在 hasClass(); ...

  4. java读取文件的几种方式性能比较

    //普通输入流读取文件内容 public static long checksumInputStream(Path filename) { try(InputStream in= Files.newI ...

  5. docker 部署oracle

    Oracle数据库服务器Docker映像文档 Oracle Database Server 12c R2是行业领先的关系数据库服务器.Oracle数据库服务器Docker映像包含在Oracle Lin ...

  6. 记录java+testng运行selenium(二)---定义元素类及浏览器

    一: 元素类 整体思路: 1. 根据状态可分可见和不可见两种 2. 同一个路径可以查找单个元素或多个元素 3. 获取元素text或者指定的value值 4. selenium对元素操作有两种,一是通过 ...

  7. 协议形式化安全分析 Scyther 并非所有协议可以照抄就搬

    1.Scyther 形式化分析工具可以对协议进行形式化描述,验证协议的机密性和可认证性是否存在安全威胁.在攻击时支持会话轮数无限次执行,同时支持在强安全模型和Delov-Yao模型.在对要形式化分析的 ...

  8. 【OF框架】在Visual Studio中启用Docker支持,编译生成,并在容器运行项目

    准备 本地已经安装Docker 一.添加Docker支持 第一步:查看本地Docker服务状态 第二步:项目添加Docker支持 第三步:选择Linux容器 第四步:点击启动 第五步:确认Docker ...

  9. [MVC] 自定义ActionSelector,根据参数选择Action[转载]

    很多时候我们会根据UI传入的参数,呈现不同的View.也就是对于同一个Action如何根据请求数据返回不同的View.通常情况下我们会按照如下方法来写,例如: [AcceptVerbs(HttpVer ...

  10. Struts2中There is no Action mapped for namespace错误解决方法

    1.我的原有配置 jsp表单提交路径 <form class="layui-form" id="form" action="${ctx }/me ...