天天看點

BestCoder Round #55 解題報告

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 = ;
int T, n;
int a[], b[];
int main()
{
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        int mx = ;
        for (int i = ; i < n; i++){
            scanf("%d", &a[i]);
            mx = max(a[i], mx);
        }
        for (int i = ; i < n; i++) scanf("%d", &b[i]);
        long long tot = ; // /3
        for (int i = ; i < n; i++) tot += (long long)b[i] * b[i] * a[i];
        double l = , r = mx, need = (double)tot/;
        while(fabs(r - l) >= eps){
            double mid = (l + r) / ;
            double sum = ;
            for (int i = ; i < n; i++){
                if (mid < a[i]){
                    double h = (double)a[i] - mid;
                    double w = b[i] * h / a[i];
                    sum += h * w * w / ;
                }
            }
            if (fabs(sum - need) < eps){ l = mid; break; }
            if (sum < need) r = mid;
            else l = mid;
        }
        printf("%d\n", (int)l);
    }
    return ;
}
           

B

Source

hdu 5433

題意

直接看題吧。。

分析

沒什麼好說的。。dp[x][y][k]表示走到(x, y),剩餘k鬥志的最小花費體力,然後相當于求最短路咯。trick在于初始鬥志為0一律無解,即使起終點重合。

代碼

/*************************************************************************
    > File Name: b.cpp
    > Author: james47
    > Mail: 
    > Created Time: Sat Sep  :: 
 ************************************************************************/

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

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

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 = ;
const int MAXN = ;
int N;
struct matrix{
    int a[MAXN][MAXN];
    friend matrix operator *(const matrix& a, const matrix& b){
        matrix ret;
        for (int i = ; i < N; i++)
            for (int j = ; j < N; j++) ret.a[i][j] = ;
        for (int i = ; i < N; i++)
            for (int j = ; j < N; j++){
                for (int k = ; 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 = ; i < N; i++)
            for (int j = ; j < N; j++) ret.a[i][j] = i==j;
        for (; exp; exp >>= ){
            if (exp&) ret = ret * tmp;
            tmp = tmp * tmp;
        }
        return ret;
    }
} mat;

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

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

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 = ;
const int mod = +;

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

int dfs(int pos, int pre, bool bound){
    if (pos == ){ return pre; }
    if (!bound && dp[pos][pre] != -) return dp[pos][pre];
    int ret = ;
    int lim = bound? d[pos]: ;
    for (int i = ; i <= lim; i++){
        int tmp = dfs(pos-, 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 = ;
    while(s[p] == '0') p++;
    int len = strlen(s);
    if (p == len) return ;
    int i, j;
    for (i = len-, j = ; i >= p; i--, j++)
        d[j] = s[i] - '0';
    j--;
    return dfs(j, , true);
}
int cal(char *s){
    int len = strlen(s);
    int ret = ;
    for (int i = ; i < len; i++)
        ret ^= s[i] - '0';
    return ret;
}

int T;
int main()
{
    memset(dp, -, sizeof(dp));
    scanf("%d", &T); int cas = ;
    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 ;
}
           

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  :: 
 ************************************************************************/

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

const int maxn = ;
const int inf = e9;
struct node{
    int w, fa, dep, s, ma, 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][];
int p[maxn][];

void work(){
    for (int i = ; i <= n; i++){
        p[i][] = a[i].fa;
        o[i][] = a[a[i].fa].w;
    }
    //突然發現這裡寫反了。。不過剛好這題不會影響。。
    //u的祖先标号都比它小。。
    for (int i = ; i <= n; i++)
        for (int j = ; j <= ; j++){
            p[i][j] = p[p[i][j-]][j-];
            o[i][j] = max(o[i][j-], o[p[i][j-]][j-]);
        }
}

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 dfs(int u){
    a[u].dep = a[a[u].fa].dep + ;
    a[u].s = inf, a[u].ma = ;
    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 != -; t = e[t].n){
        int v = e[t].v;
        dfs(v);
        if (a[v].s < a[u].s || (a[v].s == a[u].s && a[u].ma < a[v].ma)){
            a[u].s = a[v].s; a[u].ma = a[v].ma;
        }
    }
    if (a[u].s == inf) a[u].s = ;
    a[u].s += a[u].w;
    chkmax(a[u].ma, a[u].w);
}

void dfs1(int u){
    int s, ma;
    for (int t = en[u]; t != -; t = e[t].n){
        int v = e[t].v;
        s = a[u].s + a[v].w;
        ma = max(a[u].ma, a[v].w);
        if (s < a[v].s || (s == a[v].s && ma > a[v].ma)){
            a[v].s = s; a[v].ma = ma;
        }
        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 = ; i >= ; i--){
        if (a[x].dep - (<<i) >= a[y].dep){
            chkmax(t, o[x][i]);
            x = p[x][i];
        }
    }
    if (x == y) return x;
    for (int i = ; i >= ; i--){
        if (a[x].dep - (<<i) >=  && 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[].dep = -; a[].fa = ; a[].w = ;
    a[].s = a[].s1 = a[].ma1 = ;
    scanf("%d", &T);
    while(T--){
        scanf("%d %d", &n, &q);
        a[].fa = ;
        esize = ;
        memset(en, -, sizeof(int)*(n+));
        for (int i = ; i <= n; i++){
            scanf("%d", &a[i].fa);
            addedge(a[i].fa, i);
    //      addedge(i, a[i].fa);
        }
        for (int i = ; i <= n; i++){
            scanf("%d", &a[i].w);
        }
        work(); dfs(); dfs1();

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

        while(q--){
            int u, v;
            scanf("%d %d", &u, &v);
            int t, t1, lca;
            lca = cal(u, v, t1);
            t = a[u].s1 + a[v].s1 - a[lca].s1 *  + a[lca].w;
//          printf("%d %d %d\n", lca, t, t1);
            update(t, t1, a[u].s + a[v].s + a[].w, max(a[].w, max(a[u].ma, a[v].ma)));
            update(t, t1, a[u].s + a[v].s1, max(a[u].ma, a[v].ma1));
            update(t, t1, a[u].s1 + a[v].s, max(a[u].ma1, a[v].ma));
            printf("%d %d\n", t, t1);
        }
    }
    return ;
}