记2018/5/5 qbxt 测试
记2018/5/5 qbxt 测试
竞赛时间: 2018 年 5 月 5 日 13:30-17:00
T1
一、maze(1s,512MB):
简单的迷宫问题:给定一个n*m的迷宫,.表示可以通过,#表示不能通过。每一步可以向上下左右的任意方向移动,问是否能够正好k步从s走到t
输入格式:
第一行n,m表示迷宫的大小
接下来n行每行m个字符表示迷宫
接下来一行五个数字 x1,y1,x2,y2,k 表示起点为(x1,y1) 终点为(x2,y2) 步数为k
输出格式:
如果可以正好k步从s走到t输出方案,L表示向左,R表示向右,U表示向上,D表示向下。如果不能输出-1
样例:
输入:
3 3
…
.#.
…
1 1 3 3 4
输出:
RRDD
数据范围:
30%的数据满足n,m<=3 保证一定能到
60%的数据满足 n,m,k<=100
100%的数据满足 n,m<=1000 k<=1000000
思路:广搜 or 深搜 + 剪枝
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int n,m,x1,x2,y1,y2,K; char c; int g[1005][1005],f[1005][1005]; int q[20000000][3]; int answer[20000000], dir[1005][1005]; int main() { freopen("maze.in","r",stdin); freopen("maze.out","w",stdout); scanf("%d %d",&n,&m); for (int i = 1; i<=n; i++) for (int j = 1; j<=m; j++) { scanf(" %c",&c); if (c=='.') { g[i][j] = 1; } else if (c=='#') { g[i][j] = 0; } f[i][j] = -1; } scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&K); if (g[x1][y1] == 0) { printf("-1\n"); return 0; } f[x1][y1] = 0; q[1][1] = x1; q[1][2] = y1; for (int s = 1,t = 1; s<=t; s++) { int x = q[s][1], y=q[s][2]; if (x-1>0 && g[x-1][y] == 1 &&f[x-1][y] == -1) { f[x-1][y] = f[x][y]+1; t++; q[t][1] = x-1; q[t][2] = y; dir[x-1][y] = 1; } if (x+1<=n && g[x+1][y] == 1 && f[x+1][y] ==-1) { f[x+1][y] = f[x][y] +1; t++; q[t][1] = x+1; q[t][2] = y; dir[x+1][y] = 2; } if (y-1>0 && g[x][y-1]==1 && f[x][y-1] == -1) { f[x][y-1] = f[x][y]+1; t++; q[t][1] = x; q[t][2] = y-1; dir[x][y-1] = 3; } if (y+1<=m && g[x][y+1] == 1 &&f[x][y+1] == -1) { f[x][y+1] = f[x][y] +1; t++; q[t][1] = x; q[t][2] = y+1; dir[x][y+1] = 4; } } if (f[x2][y2] == -1 || (f[x2][y2]+K)%2 == 1 || f[x2][y2]>K) { printf("-1\n"); } else { int x3 = x2, y3 = y2; for (int i = 0; x2!=x1 ||y2!=y1; i++) { answer[f[x2][y2]] = dir[x2][y2]; int tmp = dir[x2][y2]; if (tmp == 1) x2++; else if (tmp == 2) x2--; else if (tmp==3) y2++; else y2--; } x2 = x3,y2=y3; for (int i =0; i<=f[x2][y2]; i++) { if (answer[i] == 1) printf("U"); else if (answer[i] == 2) printf("D"); else if (answer[i]==3) printf("L"); else if (answer[i]==4) printf("R"); } for (int i = f[x2][y2]+1; i<=K; i+=2) { if (x2+1<=n && g[x2+1][y2]==1) printf("DU"); else if (x2-1>0 && g[x2-1][y2]==1) printf("UD" ); else if (y2+1<=m && g[x2][y2+1] == 1) printf("RL"); else printf("LR"); } } return 0; }
T2
二、tree(1s,512MB):
对于一个无根树,他的任意一个联通子图称为他的一个子树。现给出一个无根树,每个节点上有一个权值。 一个子树的权值为这个子树上所有节点权值的xor和。比如子树包含有权值为(1,2,4)的节点,那么子树的权值为 1 xor 2 xor 4 = 7。给定一个无根树求所有子树的权值的和,由于这个数可能很大结果对10^9+7取余。
输入格式:
第一行一个整数n表示树的节点数目
第二行n个整数表示每个节点的权值
接下来n-1行每行两个树 a,b表示a到b有一条边 (输入保证一定是一个树)
输出格式:
一个数 表示所有子树权值的和对10^9+7取余的结果
样例:
输入
3
1 2 3
1 2
1 3
输出:
11
样例解释:
所有的子树有{1}, {2},{3},{1,2},{1,3},{1,2,3}
子树的权值为1, 2, 3, 3, 2, 0
答案为 1+2+3+3+2+0 = 11
数据范围:
30%的数据满足 n<=5
60%的数据满足 n<=100
100%的数据满足 n<=100000 树上节点的权值为正整数<=10000000000
其中有40%的数据满足节点权值=0或者1 另有其他10%的数据满足节点权值为1
思路:Xor可以拆城若干个二进制位 之间互不影响
考虑一个二进制位:
随便选一个点为根,f[i][0] 表示以 i 为根的子树 xor 和为 0 的子树个数,f[i][1] 表示以 i 为根的子树xor 和为 1 的子树
f[i][0] = f[i][0]*(f[son[i]][0] + 1) + f[i][1]*f[son[i][1]]
f[i][1] = f[i][1]*(f[son[i][0]] + 1) + f[i][0]*f[son[i][1]]
最后 for 一遍所有的节点 统计答案
f[fa[i][0]] = f[fa[i]][0]*(f[i][0] + 1) + f[fa[i][1]]*f[i][1]
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 300000; int fa[maxn],ne[maxn],h[maxn],poi[maxn],q[maxn],vis[maxn]; long long f[maxn][3]; int a[maxn]; int n,tot,x,y; int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); scanf("%d",&n); for (int i =1; i<=n; i++) scanf("%d",&a[i]); for (int i = 0; i<n-1; i++) { scanf("%d%d",&x,&y); tot++; ne[tot] = h[x]; h[x] = tot; poi[tot]= y; tot++; ne[tot]= h[y]; h[y] = tot; poi[tot]=x; } q[1] = 1; vis[1] = 1; for (int s = 1, t = 1; s<=t; s++) { int x = q[s]; for (int j = h[x]; j; j=ne[j]) if (vis[poi[j]] == 0) { fa[poi[j]] = x; t++; q[t] = poi[j]; vis[poi[j]] = 1; } } long long ans = 0; for (int t = 31; t>=0; t--) { for (int i = 1; i<=n; i++) { if (a[i]&(1<<t)) f[i][0] = 0, f[i][1] = 1; else f[i][0] = 1, f[i][1] = 0; } for (int i = n; i>=1; i--) { int k = q[i]; int tmp0 = f[fa[k]][0],tmp1 = f[fa[k]][1]; f[fa[k]][0] = (tmp0*(f[k][0]+1) + tmp1*f[k][1])%1000000007; f[fa[k]][1] = (tmp0*f[k][1] + tmp1*(f[k][0]+1))%1000000007; } for (int i =1; i<=n; i++) ans = (ans + f[i][1]*(1LL<<t))%1000000007; } printf("%lld",ans); return 0; }
T3
三、number(1s,512MB)
数字的变化一直是千奇百怪的,于是本题只研究数字的加法和乘法。给定一个数s,每次你能将s变为 s+a 或者 s*b。问最少用几步变为t,无解输出-1
输入格式:
四个数 s,t,a,b (s,t为非负整数,a,b为正整数)
输出格式:
一个数表示最少的步数
样例:
输入
5 26 3 2
输出
3
样例解释:
S = 5
S = S * B = 5*2 = 10
S = S+A = 5+3 = 13
S = S*B= 13*2 = 26 = T
所以只要3步
数据范围:
30%的数目满足 s,t,a,b<=100
60%的数据满足s,t,a,b<=1000000
100%的数据满足 s,t,a,b<=1000000000000000000
思路:S = 0, 就是将 T/a b进制分解将所有的数字加起来
枚举 x 表示 s 乘了 x 次 b 记结果为 res
将 ( T-res) / a b 进制分解,小于 x 位的数字加起来大于等于 x 位的转成十进制加。( T-res) 能够整除a
#include <cstdio> #include <cstring> using namespace std; long long s,t,a,b; int main() { freopen("number.in","r",stdin); freopen("number.out","w",stdout); scanf("%lld %lld %lld %lld",&s,&t,&a,&b); if (b==1) { if ((t-s) %a == 0) printf("%lld\n",(t-s)/a); else printf("-1\n"); return 0; } long long ans; if ((t-s) %a == 0) ans = (t-s)/a ; else ans = 1000000000000000003LL; for (int i = 1; ; i++) { if (t/s < b) break; s*=b; long long tt = t-s; if (tt%a == 0) { tt/=a; long long ans1 =0; for (int j = 1; j<=i; j++) { ans1+=tt%b; tt/=b; } ans1+=tt+i; if (ans1<ans) ans = ans1; } } if (ans != 1000000000000000003) printf("%lld",ans); else printf("-1\n"); }
希望今天前往北京参加APIO的学长学姐以及同级的同学可以考出让自己满意的成绩
不负初心,继续前进