Clone from Regional Olympiad of Student St Petersburg, October 24, 2015
题目pdf版本
主要整理了觉得有意思的题以及赛后补的题,VJ的训练赛难度确实没有多校赛高,下午的体验总体比自闭多校赛好很多了。
这次的题要用文件形式输出,前前后后因为这个整队wa了有四五发(离谱),以后细节还是要多注意。另外还有一个就是英文题面读题慢,读不懂,读错题的问题还是蛮严重的,英语阅读能力还有待加强。组队赛和单枪匹马感觉还是很不一样的,有人一起讨论动力也会比个人赛高很多,每个人都有不同的思路,大家一起头脑风暴的感觉还是挺好的。目前主要瓶颈就是英语水平和算法水平,继续努力吧!
题意:输入b 和 w,输出一个矩形图案,矩形图案由白色块(‘@’表示)和黑色块(‘.’表示)组成,要求白色四连通子块有w个,黑色四连通子块有b个。
思路:我们可以先判断黑色多还是白色多,然后用多的包围少的就行,具体包围方法如下:
@@@
@。@
@@@
@。@
@@@
。。。
@@@
。。。
@@@
可以发现这种形式的图形可以满足任何需求(最上面’日’字形的包围算一个连通块,黑白差多少就用最上面这种图形包围多少个,剩下相同的就间隔输出,具体看代码,好懂!)
AC代码:
#include
#include
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;
}
题意: 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使得 xp,为什么会这样想呢,因为拆分最简单的情况就是拆成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
AC代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
// dp+单调队列+二分答案
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}