——暨GDKOI校队选拔赛
T1: WXYZ与绿豆饼
描述
WXYZ很喜欢吃绿豆饼,而且每次都能吃很多。但是担心WXYZ长的太胖,妈妈把买回来的一卷卷的绿豆饼排成一列(每卷绿豆饼的高度可能是不一样的),规定WXYZ只能取其中一段连续的并且高度严格上升的绿豆饼来吃。
可怜的WXYZ发现,根据这个规则,每次他只能拿到很少的绿豆饼,于是他想到了一个办法:趁妈妈不注意的时候改变某一卷绿豆饼的高度(压扁或拉长它)。WXYZ知道,这样就可以拿到一段长得多的连续的而且高度严格上升的绿豆饼了。
真是太聪明了!不过应该改变哪一卷绿豆饼的高度才能使得可以拿走的连续的一段高度严格上升的绿豆饼最多呢?嗯,这个问题就交给你了,如果你能成功解答说不定WXYZ会分给你一些绿豆饼呢。 注意:修改之后绿豆饼的高度最小为1,最大为10000,并且高度只能是整数。
输入格式
第1行为一个正整数N(<=10000),表示有绿豆饼的卷数。
第2行为N个正整数,表示这N卷绿豆饼的高度,两个正整数之间会有一个空格,高度值不会大于10000
输出格式
输出一行,包括一个整数,表示修改之后最长的一段连续且高度严格上升的绿豆饼的长度。行末以回车键结束。
输入样例
6
6 1 2 2 4 4
输出样例
4
样例说明
样例中将第四个高度修改为3,这样得出的高度序列是6 1 2 3 4 4,可以拿到最长的连续严格上升序列就是1 2 3 4,长度为4
数据范围
对于30%的数据,满足1≤n≤10
对于80%的数据,满足1≤n≤100
对于100%的数据,满足1≤n≤10000
题解
一开始看错题,开开心心地写了最长上升子序列。。
好吧,令:
即从位置i往左往右延伸的最长连续上升子串距离。
如果我们在两个连续上升子串间修改一个数,那么对于每个修改的位置,然而我们不关心修改成什么值,因为对答案没有影响,但是修改的前提是 hi+1−hi−1>1 。扫一遍计 fi−1+1+ri+1 即可。
如果在一个子串前后改一个数,计 fi+1 或 ri+1 即可。
程序
#include <cstdio>
#include <algorithm>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;i++)
#define rep(i,j,k) for(i=j;i<k;i++)
#define FORD(i,j,k) for(i=j;i>=k;i--)
const int N = 10100;
int main() {
static int h[N], a[N] = {0}, c[N], f[N], r[N];
int n, i, j, ans = 0, ans_c = 0, ans_e = 0;
scanf("%d", &n);
h[0] = -(h[n + 1] = 20000); f[0] = 0; r[n + 1] = 0;
FOR(i,1,n) scanf("%d", h + i);
FOR(i,1,n) if (h[i] > h[i - 1]) f[i] = f[i - 1] + 1; else f[i] = 1;
FORD(i,n,1) if (h[i] < h[i + 1]) r[i] = r[i + 1] + 1; else r[i] = 1;
FOR(i,1,n) if (h[i + 1] - h[i - 1] > 1)
ans = max(ans, f[i - 1] + 1 + r[i + 1]);
FOR(i,2,n) if (h[i] > 1) ans = max(ans, r[i] + 1);
rep(i,1,n) if (h[i] < 10000) ans = max(ans, f[i] + 1);
printf("%d", ans);
return 0;
}
T2: 讨厌的新系统
最近,某公司推出了一种新的 PC 操作系统 Swodniw vista 。于是,小恒便高高兴兴地将它买了回来,并给自己的计算机撞上了。结果他发现许多软件和程序的运行速度都慢了很多,和以前的操作系统相比,简直就是天壤之别。后来小恒发现,原来是新系统的 UAC 保护模式在作怪。
UAC 保护模式其实就是一个用户权限检测系统。每当一个用户执行一条操作,UAC 都会花上若干时间来检测合法性。UAC 将检测分成了 N 个模块,每个模块的执行顺序严格按照一个有向树拓扑有序,如下图所示:
该有向树的意义为:你需要完成某个检测,必须在该检测之前,完成其在树上的父亲检测。
一个模块包含两个属性,匹配串 S 和检测时间 C 。现在,对一个操作串 T ,当一个模块的S包含了 T 中的某个字符,则必须执行该模块。要注意,可以同时执行的多个模块,但要保证完成了父亲模块,才能开始儿子模块的检测。
比如,对操作系列“BE”,则执行了 A、B、C、D、E 五个模块,最少话费时间为 6(1+3+2)。
现在给出 M 个操作串,那么 UAC 完成每个串的检测最少需要多少时间呢?
输入格式
第1行为两个整数 N 和 M(N≤2007,M≤100000),N 表示检测个数,M 表示操作串个数。
接下来 N 行,每行包含非负整数 F 和 C(C≤1000)和一个字符串 S(S长度≤100),分别表示前继模块的序号,检测时间和匹配串。模块由 1 到 N 编号,根的前继为 0 。
再下来 M 行,每行只有一个字符串(长度≤100),表示需要检测的操作串。
字符串中不包含空格、换行符和所有的控制字符。
输出格式
输出 M 行,每行包含一个整数,分别表示每个操作串最少的检测时间。
样例输入1
6 2
0 1 ABCDEF
1 3 ABC
2 2 BC
1 1 DEF
4 1 D
1 4 AEF
BE
AA
样例输出1
6
5
样例输入2
3 2
0 0 0
1 1 1
2 2 0
0
1
样例输出2
3
1
题解
先预处理UAC树,处理出每个模块完成需要的时间。然后对于每个字符,处理出包含这个字符的模块的最长时间。最后对于每个操作串的每个字符,查询预处理出来的结果即可。
程序
#include <cstdio>
#include <cstring>
#include <algorithm>
#define FOR(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int N = 2048, M = 8192, inf = 2147483647;
char s[N][N], op[N];
int f[N], c[N], t[N], n;
int h[N] = {0}, p[M], v[M], cnt = 0;
void add(int x, int y) {
p[++cnt] = h[x]; v[cnt] = y; h[x] = cnt;
}
void dfs(int x) {
for (int i = h[x]; i; i = p[i])
c[v[i]] += c[x], dfs(v[i]);
}
void solve() {
int m, i, j, ans, rt = 1;
scanf("%d%d", &n, &m);
FOR(i,1,n) {
scanf("%d%d", f + i, c + i);
gets(s[i]);
if (f[i] == 0) rt = i;
add(f[i], i);
}
dfs(rt);
memset(t, 0, sizeof t);
FOR(i,1,n) for(j=0;s[i][j];j++)
t[s[i][j]]=max(t[s[i][j]],c[i]);
FOR(i,1,m) {
gets(op);
ans = 0;
for(j=0;op[j];j++)
ans=max(ans,t[op[j]]);
printf("%d\n", ans);
}
}
T3: 骰神秘笈
描述
RC非常喜欢一个名叫”Lie Dice”的游戏,并立志要成为当中的牛人。后来他听说有一位成为”骰神”的世外高手,就来到了骰神的住所,想拜骰神为师。开始时骰神将他拒绝了,但在RC的死缠烂打之下,骰神终于心软,不过提出要经过一场入门考验。
骰神的考验是这样的:在骰神的园子里有一个N*M的矩形地,里面是N行M列的小方格。对每一行按从南到北的顺序由1-N编号,对每一列按从西到东的顺序由1-M编号,每个小方格用它的行列编号来表示,即(i,j)表示第i行第j列的方格。这样最西南的方格就是(1,1),最东北的方格就是(n,m)。
骰神在(1,1)的方格中放置了一颗骰子,其中点数1向上。骰子可以往东南西北四个方向滚动,滚动之后骰子就到了该方向的下一格,但同时点数1的朝向也可能会发生变化。如最开始将骰子往北滚动,骰子就到了(2,1)处,此时点数1向北。
可怜的RC想了很久,还是没有想出办法。他郁闷地往回走时,突然碰到一个乞丐。乞丐望了他两眼,惊异地说:”不得了了,不得了了。你有一道灵光从天灵盖中喷出,年纪轻轻就有一身投机的气质,真是百年难得一见的赌博奇才。若让你打开任督二脉,你还不会飞骰啊?今天看你我有缘,我这里有一本秘笈,就便宜点10块钱卖给你吧!”
RC定睛一看,这是一本名为《骰神秘笈》的方格簿。RC翻开第一页,上面赫然写着:”你还在为通不过滚骰考验而困扰吗?来吧,只要拨打以下热线号码,包你轻松过关!”
RC拨完那个号码后,你的电话突然响起,原来那是你的号码。那么,你能帮帮他吗?
Input
输入有多组数据。每组数据仅有一行,当中有两个正整数X和Y,表示要将骰子滚到(X,Y)处,且点数1向上。X=Y=0时表示输入结束。
1<=X,Y<=100000000
Output
对于每组数据输出一行,仅包含一个整数,即最少要滚动的次数。
Sample Input
1 1
1 2
1 5
0 0
Sample Output
0
3
4
题解
为了方便表述,令 x′=x+1,y′=y+1 ,下文用 x,y 代替 x′,y′ ,并令 x≤y
- x=0
- 如果 4|y ,那么显然步数为 y 。
- 否则先上再右后下,步数为
y+2 。
- x=1
- y=1, 滚两圈6步
- 4|y ,显然是 x+y 步,因为到(0,y)的步数是 y ,那么上一步就是(0,y)左的时候上就可以到了。
- 否则
x+y+2 步,同上。
- x>1
发现,同列之间各自互相等价,偶数列之间或奇数列之间分别等价(意为走曼哈顿距离步数就可以到达),(2,2)的步数是4,所以x>1,y>1的格子全部都是 x+y 。
程序
#include <cstdio>
int main() {
int x, y;
while (scanf("%d %d", &x, &y) == 2) {
if (x == 0 && y == 0) break;
if (x > y) swap(x, y);
x--, y--;
if (x == 0)
if (y % 4 == 0) printf("%d\n", y);
else printf("%d\n", y + 2);
else if (x == 1) {
if (y == 1) printf("%d\n", x + y + 4);
else if (y % 4 == 0) printf("%d\n", x + y);
else printf("%d\n", x + y + 2);
} else printf("%d\n", x + y);
}
return 0;
}
T4: 谁是天才
描述
这天张大牛遇到了大肥熊。
张大牛:“我是天才!”
大肥熊:“你为什么是天才?”
张大牛:“你随便告诉我一个数字,我立即可以算出他所有约数的和,以及所有约数的倒数和!”
大肥熊:“换过来,我告诉你一个数的所有约数(包括1和该数本身)的和,以及约数的倒数之和,你是天才你应该立即能推出这个数是什么!”
张大牛被难倒了!
现在,这个难倒了天才的题目就交到你手上了。
输入
输入文件包含多组输入数据。
每组数据有三个正整数
A,B1,B2(1≤A,B1,B2≤109)
,其中A为C的约束和,而对于C的所有约束的倒数之和B,为避免精度误差,以分数
B1/B2
的形式给出。
输入文件以一行“0 0 0”结束。
输出
对于输入的每一组数据输出一行,该行的第一个整数N是所有满足条件的不同的C的个数。其后按照从小到大的顺序输出N个数,为所有满足条件的C。相邻两个整数之间用空格隔开。
输入样例
18 9 5
1 1 2
1 1 1
0 0 0
输出样例
1 10
0
1 1
题解
我们可以发现,如果
N
的所有约数分别为
如果 B1∤A∗B2 ,那么 C 就不存在,否则还需要验证
而约数和显然不能暴力,对N质因数分解以后,有:
令 C 的约数和为
显然有:
程序
#include <cstdio>
typedef long long ll;
ll dfs(ll a, ll q) {
for (q++; ; q++)
if (a % q == 0) {
ll ans = 1, kq = 1;
while (a % q == 0)
a /= q, kq *= q, ans += kq;
if (a == 1) return ans;
else return dfs(a, q) * ans;
}
}
void work(ll a, ll b1, ll b2) {
static int q[500];
ll c = a * b2, sum = 0;
if (c % b1 == 0) {
c /= b1;
if (c == 1 && a == 1) { printf("1 1\n"); return; }
sum = dfs(c, 1);
if (sum == a) {
printf("1 %d\n", c);
return;
}
}
puts("0");
}
int main() {
int a, b1, b2;
while (1) {
scanf("%d%d%d", &a, &b1, &b2);
if (a == 0 && b1 == 0 && b2 == 0) break;
work(a, b1, b2);
}
return 0;
}