点这里进入我的博客,获得更好的体验!
搜索框中搜索这篇博客即可,已做迁移,支持一下咯~
Clone from Regional Olympiad of Student St Petersburg, October 24, 2015
前言
主要整理了觉得有意思的题以及赛后补的题,VJ的训练赛难度确实没有多校赛高,下午的体验总体比自闭多校赛好很多了。
这次的题要用文件形式输出,前前后后因为这个整队wa了有四五发(离谱),以后细节还是要多注意。另外还有一个就是英文题面读题慢,读不懂,读错题的问题还是蛮严重的,英语阅读能力还有待加强。组队赛和单枪匹马感觉还是很不一样的,有人一起讨论动力也会比个人赛高很多,每个人都有不同的思路,大家一起头脑风暴的感觉还是挺好的。目前主要瓶颈就是英语水平和算法水平,继续努力吧!
Problem B. Black and White
-
题意:输入b 和 w,输出一个矩形图案,矩形图案由白色块(‘@’表示)和黑色块(‘.’表示)组成,要求白色四连通子块有w个,黑色四连通子块有b个。
-
思路:我们可以先判断黑色多还是白色多,然后用多的包围少的就行,具体包围方法如下:
@@@
@。@
@@@
@。@
@@@
。。。
@@@
。。。
@@@
可以发现这种形式的图形可以满足任何需求(最上面’日’字形的包围算一个连通块,黑白差多少就用最上面这种图形包围多少个,剩下相同的就间隔输出,具体看代码,好懂!) -
AC代码:
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
int main() {
freopen("black.in", "r", stdin);
freopen("black.out", "w", stdout);
int n, m, i, t;
scanf("%d %d", &n, &m);
char c = '@', d = '.';
if (n > m) {
swap(n, m);
swap(c, d);
}
cout << 2*m << ' ' << 3 << endl;
for (i = 0; i < (m - n); i++)
cout << c << c << c << endl << c << d << c << endl;
for (i = 0; i < n; i++)
cout << c << c << c << endl << d << d << d << endl;
return 0;
}
Problem D. Distribution in Metagonia
-
题意: t组数据,每组输入一个n,把n拆成若干个数(可以就一个),要求每个数之间不能相互被整除且最小质因数只能是 2 或 3。
-
思路:
1.首先我们一定可以把n表示成 a·2x3y 的形式,a为不能被2和3整除的数。 然后我们把原问题就转换成把a拆开的小问题了,因为只要a能拆成几个不能相互被整除的数,那么他们分别乘上2x3y也是相互不能被整除的,满足题目要求。
2.那么我们接下来就拆a,分析可知,a是奇数(a不能被2整除),有两种情况:
如果a=1,那答案就是n。
如果a不等于1,那么我们就拆a,方法是找到一个最大的x使得 x<a,且∃p 使得x= 3p,为什么会这样想呢,因为拆分最简单的情况就是拆成3的幂或者2的幂,如果我们拆成x+y,那么y一定是偶数,那么x一定不是y的倍数(x为奇数,y为偶数),同时因为x为3的最高次幂,所以y一定也不是x的倍数(反证法:如果y是x的倍数,最小2倍,那么y能被3整除,则x不是3的最高次幂,矛盾),所以这种分法一定满足题目要求(相互不能被整除)。至此,我们可以把n表示成(x+y)·2x3y,但是y不一定能刚好表示成2x3y这种形式,所以还得继续用上面的方法:先除去2和3的幂次,再减去3的最高次幂的方法去拆,可得:n=(x+2x23y2(k+z))·2x13y1,(k+z=y,∃p 使得k= 3p,最大的k<y)同理,k和z相互不能被整除,x和(2x23y2)k和(2x23y2)z也不能相互被整除(因为y为偶数,所以2x23y2中至少有个2)故,这种不断取最高三次+偶数的方法一定都能满足条件,并且一定能拆完(因为是奇数+偶数的形式) -
AC代码:
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define LL long long
using namespace std;
const int maxn = 1e5+10;
LL a[50];
int tot;
void cal(LL n, LL m) {
while (n % 2 == 0) m *=2, n/=2;
while (n % 3 == 0) m *=3, n/=3;
if (n == 1) {
a[tot ++] = m;
return;
}
LL x = 3;
while (x * 3 < n) x *=3;
a[tot ++] = x*m;
cal(n-x, m);
}
int main(int argc, char const *argv[]) {
freopen("distribution.in", "r", stdin);
freopen("distribution.out", "w", stdout);
int t;
cin >> t;
while (t--) {
LL n;
cin >> n;
tot = 0;
cal(n, 1);
cout << tot << endl;
cout << a[0];
for (int i=1; i<tot;i++) {
cout <<' '<< a[i];
}
cout << endl;
}
return 0;
}
Problem J. Journey to the “The World’s Start”
- 题意:输入一行n和t,表示有编号为1-n的地铁站台,需要在时间t内从1号站台乘坐到n号站台。第二行输入n-1个数pi表示第i种车票的价格,其中第i种车票可以从当前站台now乘坐到now±i的站台。第三行有n-2个数字依次表示2-n-1号站台下车再上车所需的时间,1号和n号站台上下车不花时间,地铁的速度是每经过一个站台花费单位1的时间。题目要求的是购买一张车票,要求在规定时间内从1-n站并且票价最低。
- 思路:由于只买一张车票,那么最直观的想法就是先求出每张车票从1-n所花费的最少时间是多少,然后在满足时间要求的车票里选择票价最低的就行了。这里主要有两个步骤:1.求每张车票的最短时间。2.找到满足要求的车票。
1.每张车票的最短时间,用dp[i]记录到站点i的最短时间,易得状态转移方程:dp[i]=min(dp(i-j))+v[i] 1<=j<=i-k (j取遍1-i-k的所有数)但是这样我们要计算n-1张车票,每张车票要遍历n个站台,每个站台还要往前遍历k个长度,时间复杂度为O(n3) 不超时我和你姓。那么怎么办?观察状态转移方程我们可以发现最佳答案存在于dp数组的下标区间k~i-1中,所以我们可以用一个单调队列维护这个区间来找区间内的最小值,这样计算所有车票的最小花费时间的时间复杂度为O(n)
2.我们能发现如果 坐n站的车票,能够在t时间内到达 。那么 坐n+1站的车票,也能够在t时间内到达。这样我们就可以二分查找所需时间小于等于t-(n-1)的车票中能坐站数最少的车票就好了,即规定时间内能够到达的车票分界线,最后只需遍历分界线右边所有车票找到所需票价最小的就行,这一步骤的时间复杂度为O(logn),加上计算最小时间花费,总时间复杂度为O(nlogn) - AC代码:
// dp+单调队列+二分答案
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define LL long long
using namespace std;
const int maxn = 1e5 + 10, inf = 0x3f3f3f3f;
int dp[maxn], p[maxn], v[maxn], n, m;
struct node {
int pos, val;
} q[maxn];
int check(int k) {
memset(dp, inf, sizeof(dp));
int head = 1, tail = 0;
for (int i = 1; i <= k; i++) {
dp[i] = v[i];
while (head <= tail && q[tail].val >= dp[i]) tail--;
q[++tail].val = dp[i];
q[tail].pos = i;
}
for (int i = k; i <= n; i++) {
while (i - q[head].pos > k) head++;
dp[i] = min(dp[i], dp[q[head].pos] + v[i]);
while (head <= tail && q[tail].val >= dp[i]) tail--;
q[++tail].val = dp[i];
q[tail].pos = i;
}
return dp[n];
}
int main(int argc, char const *argv[]) {
freopen("journey.in", "r", stdin);
freopen("journey.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n - 1; i++) scanf("%d", &p[i]);
for (int i = 2; i <= n - 1; i++) scanf("%d", &v[i]);
v[1] = v[n] = 0;
int l = 1, r = n - 1;
m = m - (n - 1);
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid) <= m) {
r = mid - 1;
} else
l = mid + 1;
}
int ans = inf;
for (int i = l; i <= n - 1; i++) {
ans = min(ans, p[i]);
}
printf("%d\n", ans);
return 0;
}