您的位置:首页 > 其它

BestCoder Round #55 解题报告

2015-09-14 23:53 232 查看

A

Source

hdu 5432

题意

给你几个底面是正方形的锥体(金字塔),平放于地面,水平切一刀,问切在多高的位置可以使得分离开的两部分体积之和相同。

分析

显然是有单调性的,直接二分高度就好了。

其他人求体积的做法好像很厉害,待会研究下。。

代码

/*************************************************************************
> File Name: a.cpp
> Author: james47
> Mail:
> Created Time: Sat Sep 12 19:15:02 2015
************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const double eps = 1e-5;
int T, n;
int a[11000], b[11000];
int main()
{
scanf("%d", &T);
while(T--){
scanf("%d", &n);
int mx = 0;
for (int i = 0; i < n; i++){
scanf("%d", &a[i]);
mx = max(a[i], mx);
}
for (int i = 0; i < n; i++) scanf("%d", &b[i]);
long long tot = 0; // /3
for (int i = 0; i < n; i++) tot += (long long)b[i] * b[i] * a[i];
double l = 0.0, r = mx, need = (double)tot/6;
while(fabs(r - l) >= eps){
double mid = (l + r) / 2.0;
double sum = 0;
for (int i = 0; i < n; i++){
if (mid < a[i]){
double h = (double)a[i] - mid;
double w = b[i] * h / a[i];
sum += h * w * w / 3;
}
}
if (fabs(sum - need) < eps){ l = mid; break; }
if (sum < need) r = mid;
else l = mid;
}
printf("%d\n", (int)l);
}
return 0;
}


B

Source

hdu 5433

题意

直接看题吧。。

分析

没什么好说的。。dp[x][y][k]表示走到(x, y),剩余k斗志的最小花费体力,然后相当于求最短路咯。trick在于初始斗志为0一律无解,即使起终点重合。

代码

/*************************************************************************
> File Name: b.cpp
> Author: james47
> Mail:
> Created Time: Sat Sep 12 19:40:44 2015
************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const double eps = 1e-6;
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
struct node{
int x, y, k;
node(){}
node(int x, int y, int k): x(x), y(y), k(k){}
};
const int mod = 55*55*55*4;
int T;
char mp[55][55];
int n, m, tot;
int stx, sty, edx, edy;
double dp[55][55][55];
bool inq[55][55][55];
node q[mod+5];
int main()
{
scanf("%d", &T);
while(T--){
scanf("%d %d %d", &n, &m, &tot);
for (int i = 0; i < n; i ++) scanf("%s", mp[i]);
scanf("%d %d %d %d", &stx, &sty, &edx, &edy);
if (tot == 0){
puts("No Answer");
continue;
}
stx --, sty --, edx --, edy --;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
for (int k = 0; k <= tot; k++){
dp[i][j][k] = 1e10;
inq[i][j][k] = false;
}
int head = 0, tail = 0;
dp[stx][sty][tot] = 0.0; inq[stx][sty][tot] = true;
q[tail++] = node(stx, sty, tot);
while(head != tail){
node u = q[head++]; if (head == mod) head = 0;
for (int i = 0; i < 4; i++){
int x = u.x + dx[i], y = u.y + dy[i];
if (x < 0 || y < 0 || x >= n || y >= m) continue;
if (mp[x][y] == '#') continue;
//      printf("%d\n", u.k);
//      printf("%.2f\n", (double)abs(mp[x][y] - mp[u.x][u.y])/u.k);
double tmp = dp[u.x][u.y][u.k] + (double)abs(mp[x][y] - mp[u.x][u.y])/u.k;
if (tmp + eps < dp[x][y][u.k-1]){
dp[x][y][u.k-1] = tmp;
if (x != edx || y != edy){
if (u.k-1 != 0 && !inq[x][y][u.k-1]){
inq[x][y][u.k-1] = true;
q[tail++] = node(x, y, u.k-1);
if (tail == mod) tail = 0;
}
}
}
}
inq[u.x][u.y][u.k] = false;
}
double ans = 1e10;
for (int i = 0; i <= tot; i++)
ans = min(ans, dp[edx][edy][i]);
if (fabs(ans - 1e10) < eps) puts("No Answer");
else printf("%.2f\n", ans);
}
return 0;
}


C

Source

hdu 5434

题意

出题事故!所以后面的题意没有卵用:在n*m的棋盘上放小象,小象可以攻击四个斜方向的位置。如果有公共边,可以变为合体象,相当于攻击范围变小。合法方案要求摆放的象不会互相攻击,求方案数。n很大,m<=7。

分析

说了一堆实际上都是扯淡。。

因为按照题意这种是合法的,P为象,S不放:

PPP

PSP

PPS

按照数据范围肯定是状压dp然后矩阵快速幂,但是如果这种摆放合法,状态就比较麻烦,还得记录连通性。

但实际上不用。。两行之间只要考虑斜对着的位置。。(i, j)和(i+1, j+1)同时放,必须有(i+1, j)或者(i, j+1)。大概就是这样。。题目出得不太对。。

代码

/*************************************************************************
> File Name: hdu_5434.cpp
> Author: james47
> Mail:
> Created Time: Mon Sep 14 23:00:50 2015
************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int mod = 1000000007;
const int MAXN = 130;
int N;
struct matrix{
int a[MAXN][MAXN];
friend matrix operator *(const matrix& a, const matrix& b){
matrix ret;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++) ret.a[i][j] = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++){
for (int k = 0; k < N; k++){
ret.a[i][k] = ((long long)a.a[i][j] * b.a[j][k] + ret.a[i][k]) % mod;
}
}
return ret;
}
matrix pow(int exp){
matrix ret, tmp = *this;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++) ret.a[i][j] = i==j;
for (; exp; exp >>= 1){
if (exp&1) ret = ret * tmp;
tmp = tmp * tmp;
}
return ret;
}
} mat;

int n, m;
bool ok(int x, int y){
for (int i = 0; i < m; i++){
if (x&(1<<i)){
if (i != 0){
if ((y&(1<<i-1)) && !(y&(1<<i)) && !(x&(1<<i-1))) return 0;
}
if (i != m-1){
if ((y&(1<<i+1)) && !(y&(1<<i)) && !(x&(1<<i+1))) return 0;
}
}
}
return 1;
}

int main()
{
while(scanf("%d %d", &n, &m) != EOF){
N = 1 << m;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++){
if (ok(i, j)) mat.a[i][j] = 1;
else mat.a[i][j] = 0;
}
mat = mat.pow(n);
int ans = 0;
for (int i = 0; i < N; i++){
ans += mat.a[i][0];
if (ans >= mod) ans -= mod;
}
printf("%d\n", ans);
}
return 0;
}


D

Source

hdu 5435

题意

对整数x,定义F(x)为其十进制各位的异或和。给a和b,求a到b所有数的F(x)的和。a和b长度小于100000。

分析

首先容易发现 0 <= F(x) <= 15。然后我们统计[0, A]每个F(x)的值各有多少个x就好了。

做法一:

dp[len][pre]表示[0, 10^len)的数,之前高位异或和为pre,最后得到的异或和为[0..15]的数有多少个。也就是说每个dp[i][j]是一个int数组。时间复杂度O(10^5 * 16 * 9)。然后MLE。。

做法二:

dp[len][pre]表示[0, 10^len)的数,之前高位异或和为pre,最后得到异或和为0的数有多少个。相当于调用16次dfs函数,每次不同的初始pre。时间复杂度相同,内存除以16。感觉很对,结果系统测试TLE。。T.T。。出题人太坑爹,case数也需要算到复杂度里。。

做法三:

可以感觉到上面的做法都不太优。

放弃统计F(x) = [0..15]的数分别有多少个。直接算[0, A]的F(x)的和。

dp[len][pre]表示[0, 10^len)的数,之前高位异或和为pre,最后对答案的贡献的和。时间复杂度O(10^5*9),算上25组case也没有问题。。

然后发现这题的数据有前缀0,可能有人会因为这个导致错,提醒一下。。

代码

/*************************************************************************
> File Name: d.cpp
> Author: james47
> Mail:
> Created Time: Sat Sep 12 20:15:56 2015
************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int maxn = 101000;
const int mod = 1e9+7;

int d[maxn];
char sa[maxn], sb[maxn];
int dp[maxn][16];

int dfs(int pos, int pre, bool bound){
if (pos == 0){ return pre; }
if (!bound && dp[pos][pre] != -1) return dp[pos][pre];
int ret = 0;
int lim = bound? d[pos]: 9;
for (int i = 0; i <= lim; i++){
int tmp = dfs(pos-1, pre^i, bound&&i==lim);
ret += tmp;
if (ret >= mod) ret -= mod;
}
if (!bound){ dp[pos][pre] = ret; }
return ret;
}

int solve(char* s){
int p = 0;
while(s[p] == '0') p++;
int len = strlen(s);
if (p == len) return 0;
int i, j;
for (i = len-1, j = 1; i >= p; i--, j++)
d[j] = s[i] - '0';
j--;
return dfs(j, 0, true);
}
int cal(char *s){
int len = strlen(s);
int ret = 0;
for (int i = 0; i < len; i++)
ret ^= s[i] - '0';
return ret;
}

int T;
int main()
{
memset(dp, -1, sizeof(dp));
scanf("%d", &T); int cas = 0;
while(T--){
scanf("%s %s", sa, sb);
printf("Case #%d: ", ++cas);
int res = (solve(sb) - solve(sa) + cal(sa) + mod) % mod;
printf("%d\n", res);
}
return 0;
}


E

Source

hdu 5436

题意

一棵树,点有权值,叶子和根连通。每次询问两个点u和v,问u到v的路径,路径上点权和最小,顺带求上面的最大点权。多个最小时,让路径上最大点权最大。

分析

可以直接看官方题解。。

最基本的路径就是走到lca,没有修改啥的,可以倍增。。

然后可能是u走到叶子,然后从根走到v。

还有一种容易被忘记。。u走到叶子到根,再到另一个叶子,通过这个叶子到v。。

所以我们先dfs一遍,求出每个点到根的距离和,路径上最大点权等等,并求出到子树哪个叶子最近,第二次dfs要用。

然后还得再dfs一遍,求每个点离哪个叶子最近,简单的树形dp。

也可以直接用Dijkstra。。

代码很丑。。不推荐看。。

代码

/*************************************************************************
> File Name: hdu_5436.cpp
> Author: james47
> Mail:
> Created Time: Fri Sep 18 20:49:53 2015
************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int maxn = 51000;
const int inf = 2e9;
struct node{
int w, fa, dep, s0, ma0, s1, ma1;
} a[maxn];
struct edge{
int v, n;
};
int n, q;
inline void chkmax(int& a, int b){ if (b > a) a = b; }

int o[maxn][17];
int p[maxn][17];

void work(){
for (int i = 0; i <= n; i++){
p[i][0] = a[i].fa;
o[i][0] = a[a[i].fa].w;
}
//突然发现这里写反了。。不过刚好这题不会影响。。
//u的祖先标号都比它小。。
for (int i = 1; i <= n; i++)
for (int j = 1; j <= 16; j++){
p[i][j] = p[p[i][j-1]][j-1];
o[i][j] = max(o[i][j-1], o[p[i][j-1]][j-1]);
}
}

int esize;
int en[maxn];
edge e[maxn*2];
void addedge(int u, int v){
e[esize].v = v;
e[esize].n = en[u];
en[u] = esize ++;
}

void dfs0(int u){
a[u].dep = a[a[u].fa].dep + 1;
a[u].s0 = inf, a[u].ma0 = 0;
a[u].s1 = a[a[u].fa].s1 + a[u].w;
a[u].ma1 = max(a[u].w, a[a[u].fa].ma1);
for (int t = en[u]; t != -1; t = e[t].n){
int v = e[t].v;
dfs0(v);
if (a[v].s0 < a[u].s0 || (a[v].s0 == a[u].s0 && a[u].ma0 < a[v].ma0)){
a[u].s0 = a[v].s0; a[u].ma0 = a[v].ma0;
}
}
if (a[u].s0 == inf) a[u].s0 = 0;
a[u].s0 += a[u].w;
chkmax(a[u].ma0, a[u].w);
}

void dfs1(int u){
int s0, ma0;
for (int t = en[u]; t != -1; t = e[t].n){
int v = e[t].v;
s0 = a[u].s0 + a[v].w;
ma0 = max(a[u].ma0, a[v].w);
if (s0 < a[v].s0 || (s0 == a[v].s0 && ma0 > a[v].ma0)){
a[v].s0 = s0; a[v].ma0 = ma0;
}
dfs1(v);
}
}

inline void update(int &a, int &b, int c, int d){
//  printf("%d %d\n", c, d);
if (a > c || (a == c && b < d)) a = c, b = d;
}

int cal(int x, int y, int& t){
t = max(a[x].w, a[y].w);
if (a[x].dep < a[y].dep) swap(x, y);
for (int i = 16; i >= 0; i--){
if (a[x].dep - (1<<i) >= a[y].dep){
chkmax(t, o[x][i]);
x = p[x][i];
}
}
if (x == y) return x;
for (int i = 16; i >= 0; i--){
if (a[x].dep - (1<<i) >= 0 && p[x][i] != p[y][i]){
chkmax(t, o[x][i]);
chkmax(t, o[y][i]);
x = p[x][i]; y = p[y][i];
}
}
chkmax(t, a[a[x].fa].w);
return a[x].fa;
}

int T;
int main()
{
a[0].dep = -1; a[0].fa = 0; a[0].w = 0;
a[0].s0 = a[0].s1 = a[0].ma1 = 0;
scanf("%d", &T);
while(T--){
scanf("%d %d", &n, &q);
a[1].fa = 0;
esize = 0;
memset(en, -1, sizeof(int)*(n+1));
for (int i = 2; i <= n; i++){
scanf("%d", &a[i].fa);
addedge(a[i].fa, i);
//      addedge(i, a[i].fa);
}
for (int i = 1; i <= n; i++){
scanf("%d", &a[i].w);
}
work(); dfs0(1); dfs1(1);

//      for (int i = 1; i <= n; i++)
//          printf("%d %d %d %d\n", a[i].s0, a[i].ma0, a[i].s1, a[i].ma1);

while(q--){
int u, v;
scanf("%d %d", &u, &v);
int t0, t1, lca;
lca = cal(u, v, t1);
t0 = a[u].s1 + a[v].s1 - a[lca].s1 * 2 + a[lca].w;
//          printf("%d %d %d\n", lca, t0, t1);
update(t0, t1, a[u].s0 + a[v].s0 + a[1].w, max(a[1].w, max(a[u].ma0, a[v].ma0)));
update(t0, t1, a[u].s0 + a[v].s1, max(a[u].ma0, a[v].ma1));
update(t0, t1, a[u].s1 + a[v].s0, max(a[u].ma1, a[v].ma0));
printf("%d %d\n", t0, t1);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: