天天看點

2013/7/17 HNU_訓練賽5

sgu 542 Gena vs Petya

sgu 543 Cafe

題意:有N組人需要被配置設定到某些固定了人數的桌子上,其中ai表示第i組有多少個人,安排作為需要符合如下安排:某一組的人員不能夠單獨在一張桌子,每張桌子坐的人數不能夠超過其限制,問至少要安排多少張給定了人數限制的桌子。

分析:此題是一個貪心題,如果直接求解的話,由于桌子的數量是未知的,是以不太好配置設定,是以二分桌子的數量,在已知桌子的數量後運用貪心規則來求得最優解。但盡管在二分已知桌子的情況下還是不太好實作這個貪心規則。其思路是這樣的:

1.如果桌子的限制為奇數,那麼想辦法把這些的桌子用奇數人數的組分出1個3來填,使得其限制變成偶數,這樣是為了更好的填滿奇數限制的桌子;

2.如果沒有奇數人數的組,那麼找出總人數大于等于6的組,分出盡可能多的6出來,把這個6分成2個3來填奇數限制的桌子,總而言之就是盡可能使得奇數限制的桌子能夠被奇數的人數恰好填滿;

3.處理完桌子後,就是統計各組人數的情況,如果還有奇數組,那麼拿出一個3作為3人組,其餘均變為2人組,最後統計一共有多少個3人組多少個2人組,這樣做之後就使得不同組能夠統一處理;

4.最後就是把這些3人組以及2人組配置設定到修正後的桌子上,先成對成對的放置3人組,這樣能夠使得盡可能的按照偶數配置設定,因為如果存在三人組,那麼修正的桌子中一定不存在奇數限制的桌子(否則肯定會被奇數組的人填),不能成對配置設定之後再單個單個的配置設定,最後再配置設定2人組,如果都能夠放下則說明二分的這個解成立,否則不成立。

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

const int N = 2005;
int n, r;
int a[N], b[N];
struct E {
    int vol, num;
    E(int x, int y): vol(x), num(y) {}
};
vector<E>v;

bool Ac(int M) { // 判定M張桌子能否完成安排
    int x, y; // x表示容量為偶數的桌子數量,y表示容量為奇數的桌子數量 
    if (r & 1) x = 0, y = M;
    else x = M, y = 0;
    memcpy(b, a, sizeof (a));
    v.clear();
    // 以下用來使得桌子盡可能多的轉化為容量為偶數的桌子 
    if (r & 1) {
        for (int i = 0; i < n && y; ++i) { // 首先将奇數人中的3填到奇數容量的桌子中 
            if (b[i] & 1) {
                b[i] -= 3, y -= 1, x += 1;
            }
        }
        for (int i = 0; i < n && y >= 2; ++i) { // 隻有當所有的奇數組人數都比對後且奇數桌有剩餘時才會執行此處 
            while (b[i] >= 6 && y >= 2) {
                b[i] -= 6, y -= 2, x += 2;
            }
        }
        // 如果還有奇數容量的桌子就一定沒有奇數組的人或者是組員數大于6的偶數組 
        // 如果還有奇數個組員的組就一定沒有奇數容量的桌子 
        if (x) v.push_back(E(r-3, x));
        if (y) v.push_back(E(r, y));
    } else {
        v.push_back(E(r, x));
    }
    // 接下來求剩下各組人數的奇偶情況,p2、p3用來表示被分成2人組合3人組的組數 
    int p3 = 0, p2 = 0;
    for (int i = 0; i < n; ++i) {
        if (b[i] & 1) {
            b[i] -= 3, p3 += 1;
        }
        p2 += b[i] / 2;
    }
    // 先開始放置偶數對3人組,是因為這樣不會使得桌子容量退化為奇數
    for (int i = 0; i < v.size() && p3 >= 2; ++i) {
        if (v[i].vol >= 6) {
            int t1 = v[i].vol / 6; // 每個剩餘的偶數桌子能夠容納多少對3
            int t2 = min(v[i].num, p3/(2*t1)); // 已有的3人組能夠占用多少張該桌子
            if (t2 > 0) {
                p3 -= t2*t1*2;
                v[i].num -= t2;
                v.push_back(E(v[i].vol-6*t1, t2));
            }
            if (v[i].num > 0 && p3 >= 2) {
                t2 = min(t1, p3/2);
                if (t2 > 0) {
                    p3 -= t2*2;
                    v[i].num -= 1;
                    v.push_back(E(v[i].vol-6*t2, 1));
                }
            }
        }
    }
    // 能夠減掉偶數對3人組已經排除,是以隻剩下一次減掉一次三人組的情況 
    for (int i = 0; i < v.size() && p3 > 0; ++i) {
        if(v[i].vol >=3 && v[i].num > 0) {
            int tmp = min(v[i].num, p3);
            p3 -= tmp;
            v[i].num -= tmp;
            v.push_back(E(v[i].vol-3, tmp));
        }
    }
    if (p3 <= 0) {
        for (int i = 0; i < (int)v.size() && p2 > 0; ++i) {
            p2 -= (v[i].vol/2)*v[i].num;
        }
    }
    return p3 <= 0 && p2 <= 0;
}

int main() {
    while (scanf("%d %d", &n, &r) != EOF) {
        int ret;
        for (int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
        }
        int l = 1, r = n * 2000;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (Ac(mid)) {
                ret = mid;
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        printf("%d\n", ret);
    }
    return 0;
}      

View Code

sgu 544 Chess Championship

題意:類似于田忌賽馬的一道題目,A、B雙方分别有N位選手,每位選手的能力值被表示成不同的整數,2N位選手的能力值均不相同,現在問A赢B組K場的情況有多少種,也即能夠安排出多少種一一對決的方式使得A組選手正好赢B組選手K次。

分析:設A組赢的次數為x,B組赢的次數為y,則由已知條件得x+y = N, x-y = K. 可以解出x和y值,如果不存在合理的解,那麼輸出0。原本自己想的是開設dp[i][j]表示A上場能力值第i大選手時赢得場數為j的方案數,但是很快覺得這個狀态無法進行轉移,因為僅僅單方面考慮A組上場順序無法使得前後狀态順利遞推,例如前i位的方案數中不同的方案對于i+1位選手的選擇均不同。那麼如何解決該問題呢,加狀态,把A、B的選手情況通過狀态完全的表示出來,那麼這裡細細一算會發現空間複雜度提高到了500^3,有超記憶體的風險,幸好正确的狀态方程可以使用滾動數組來實作,是以在省下一維的情況下,還是能夠把這個狀态轉移完成的。

為了表示友善,暫時不采用滾動數組來表示狀态,先把A、B兩組選手一起進行排序,第一維的i表示的就是這個新的數組的下标。設dp[i][j][k]表示到第i位選手為止,A組選手赢了j場,B組選手赢了k場的方案數。那麼有狀态轉移方程:

如果是第i位選手為A組選手:dp[i][j][k] = dp[i-1][j-1][k] * (sb[i]-(j-1+k)) + dp[i-1][j][k];

否則:dp[i][j][k] = dp[i][j][k-1] * (sa[i]-(j-1+k)) + dp[i-1][j][k]. 其中sa[i]、sb[i]表示前i位選手中分别有多少位A組、B組選手,sa[i]+sb[i] = i.

方程表示這個狀态可以通過兩個方式轉移過來,第一種是第i位選手去赢前面還沒有比對的對手,因為已經排序的序列,是以未比對的對手一定會輸給目前選手;第二種選擇是将目前選手變為未比對的選手。這種狀态的表示方式觀察的是兩組選手的前若幹位選手的狀态,而不是單單的A組或者是B組選手的狀态,是以得到了正确的答案。在處理的時候還要注意某些不合法的狀态不能夠進行計算。例如目前若是A組選手,那麼赢得的場數應該是min(sa[i], sb[i]),反正就是在剩下的選手中發生比對的對數,其餘的隻有剩下來。

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

const int N = 505; 
const int mod = int(1e9)+9;
int n, k;
int sa[N<<1], sb[N<<1];
int f[2][N][N];
struct Node {
    int who, val;
    bool operator < (const Node &other) const {
        return val < other.val;
    }
}e[N<<1];

void gao() {
    int LIM = n << 1;
    int fa = (n+k)%2 ? -1 : (n+k)/2;
    int fb = (n-k)%2 ? -1 : (n-k)/2;
    if (n < k || !(~fa) || !(~fb)) {
        puts("0");
        return;
    }
    for (int i = 1; i <= LIM; ++i) {
        if (!e[i].who) {
            sa[i] = sa[i-1] + 1;
            sb[i] = sb[i-1];
        } else {
            sa[i] = sa[i-1];
            sb[i] = sb[i-1] + 1;
        }
    }
    int cur = 0;
    f[!cur][0][0] = 1;
    for (int i = 1; i <= LIM; ++i) {
        int la = min(sa[i], sb[i]);
        for (int j = 0; j <= la; ++j) {
            int lb = min(sb[i]-j, sa[i]-j);
            for (int k = 0; k <= lb; ++k) {
                if (!e[i].who) { // 如果是a的選手
                    if (j > 0)
                        f[cur][j][k] = (1LL*f[cur][j][k] + 1LL*f[!cur][j-1][k] * (sb[i] - (j-1+k))) % mod;
                    f[cur][j][k] = (1LL*f[cur][j][k] + f[!cur][j][k]) % mod;
                } else { // 如果是b的選手 
                    if (k > 0)
                        f[cur][j][k] = (1LL*f[cur][j][k] + 1LL*f[!cur][j][k-1] * (sa[i] - (j-1+k))) % mod;
                    f[cur][j][k] = (1LL*f[cur][j][k] + f[!cur][j][k]) % mod;
                }
            //    printf("f[%d][%d][%d] = %d\n", i, j, k, f[cur][j][k]);
            //    getchar();
            }
        }
        cur = !cur;
        memset(f[cur], 0, sizeof (f[cur]));
    }
//    printf("fa = %d, fb = %d\n", fa, fb);
    printf("%d\n", f[!cur][fa][fb]);
}

int main() {
    while (scanf("%d %d", &n, &k) != EOF) {
        memset(f, 0, sizeof (f));
        memset(sa, 0, sizeof (sa));
        memset(sb, 0, sizeof (sb));
        for (int i = 1; i <= n; ++i) {
            e[i].who = 0;
            scanf("%d", &e[i].val);
        }
        for (int i = 1; i <= n; ++i) {
            e[n+i].who = 1;
            scanf("%d", &e[n+i].val);
        }
        sort(e+1, e+(n<<1)+1);
        gao();
    }
    return 0;
}      

View Code

sgu 545 Cut the rope, another rope and so on!

sgu 546 Ternary Password

題意:給定長度為N的一個隻有0、1、2組成的密碼,現在要求将密碼變成一個0的數量為a,1的數量為b的密碼串要需要多少次操作,題中隻定義了一種操作,即替換操作:能夠将原串中的某一位變成另一位數字。最後輸出修改後的密碼串。

分析:dp[i][j][j]表示前i為包含j個0,k個1所需要的最少的替換次數為多少。使用dfs輸出最後結果,相當于重新做了一個動态規劃。是以有動态規劃方程:

當i位置為0時,

2013/7/17 HNU_訓練賽5

,其餘位置類似。   事後,被告知該題就是簡單的模拟題,直接統計下0、1、2的個數即可,囧了。

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

int f[205][205][205];

int N, a, b;
char str[205];

void dfs(int i, int j, int k) {
    if (i == 0) return;
    if (str[i] == '0') {
        if (j - 1 >= 0 && f[i][j][k] == f[i-1][j-1][k]) dfs(i-1, j-1, k);
        else if (k - 1 >= 0 && f[i][j][k] == f[i-1][j][k-1] + 1) str[i] = '1', dfs(i-1, j, k-1);
        else if (f[i][j][k] == f[i-1][j][k] + 1) str[i] = '2', dfs(i-1, j, k);
    } else if (str[i] == '1') {
        if (j - 1 >= 0 && f[i][j][k] == f[i-1][j-1][k] + 1) str[i] = '0', dfs(i-1, j-1, k);
        else if (k - 1 >= 0 && f[i][j][k] == f[i-1][j][k-1]) dfs(i-1, j, k-1);
        else if (f[i][j][k] == f[i-1][j][k] + 1) str[i] = '2', dfs(i-1, j, k);
    } else {
        if (j - 1 >= 0 && f[i][j][k] == f[i-1][j-1][k] + 1) str[i] = '0', dfs(i-1, j-1, k);
        else if (k - 1 >= 0 && f[i][j][k] == f[i-1][j][k-1] + 1) str[i] = '1', dfs(i-1, j, k-1);
        else if (f[i][j][k] == f[i-1][j][k]) dfs(i-1, j, k);
    }
}

void gao() {
    memset(f, 0x3f, sizeof (f));
    f[0][0][0] = 0;
    for (int i = 1; i <= N; ++i) {
        for (int j = 0; j <= i; ++j) {
            for (int k = 0; k <= i-j; ++k) {
                int Min = 100000;
                if (str[i] == '0') {
                    if (j - 1 >= 0)
                        Min = min(Min, f[i-1][j-1][k]);
                    if (k - 1 >= 0)
                        Min = min(Min, f[i-1][j][k-1] + 1);
                    Min = min(Min, f[i-1][j][k] + 1);
                } else if (str[i] == '1') {
                    if (j - 1 >= 0)
                        Min = min(Min, f[i-1][j-1][k] + 1);
                    if (k - 1 >= 0)
                        Min = min(Min, f[i-1][j][k-1]);
                    Min = min(Min, f[i-1][j][k] + 1);
                } else {
                    if (j - 1 >= 0)
                        Min = min(Min, f[i-1][j-1][k] + 1);
                    if (k - 1 >= 0)
                        Min = min(Min, f[i-1][j][k-1] + 1);
                    Min = min(Min, f[i-1][j][k]);
                }
                f[i][j][k] = Min;
            }
        }
    }
    printf("%d\n", f[N][a][b]);
    dfs(N, a, b);
    for (int i = 1; i <= N; ++i) {
        printf("%c", str[i]);
    }
    puts("");
}

int main() {
    while (scanf("%d %d %d", &N, &a, &b) != EOF) {
        scanf("%s", str+1);
        if (strlen(str+1) != N || a + b > N) {
            puts("-1");
            continue;
        }
        gao();
    }
    return 0;
}      

View Code

sgu 547 Divide the Kingdom

sgu 548 Dragons and Princesses

題意:給定N個格子,編号為1-N,騎士現在在1号格子,2-N号格子中有惡龍或者是公主,如果是惡龍的話,那麼戰勝每條惡龍能夠得到一定的金币,如果是公主,那麼之前斬殺的惡龍數量如果大于或者等于該公主的要求,則這個公主就嫁給騎士。騎士之喜歡位于編号為N格子中的公主,是以騎士必須不滿足前面任何一個公主的要求,并且使得最後所赢得的金币最多。

分析:從前往後,選擇一個優先隊列,每次遇到惡龍,加入到隊列中,如果遇到公主,則把金币小的惡龍全部退出來,然後再接着斬殺惡龍,最後一個公主不需要處理,如果最後斬殺的惡龍不足以滿足最後一個公主的要求,輸出-1。

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

int N;

struct Node {
    char str[5];
    int val, No;
    bool operator < (const Node & other) const {
        return val > other.val;
    }
}e[200005];

priority_queue<Node>q;
vector<Node>v;

bool cmp(const Node &a, const Node &b) {
    return a.No < b.No;
}

int main() {
    while (scanf("%d", &N) != EOF) {
        v.clear();
        for (int i = 2; i <= N; ++i) {
            e[i].No = i;
            scanf("%s %d", e[i].str, &e[i].val);
        }
        for (int i = 2; i < N; ++i) {
            if (e[i].str[0] == 'p') {
                while (q.size() >= e[i].val) q.pop();
            } else {
                q.push(e[i]);
            }
        }
        if (q.size() < e[N].val) {
            puts("-1");
            continue;
        }
        int tot = 0;
        while (!q.empty()) {
            v.push_back(q.top());
            tot += q.top().val;
            q.pop();
        }
        sort(v.begin(), v.end(), cmp);
        printf("%d\n%d\n", tot, v.size());
        for (int i = 0; i < (int)v.size(); ++i) {
            printf(i == 0 ? "%d" : " %d", v[i].No);
        }
        puts("");
    }
    return 0;
}       

View Code

sgu 549 Dumbbells

題意:給定了若幹個啞鈴,現在要求将這些啞鈴分組出售,每組啞鈴必須要由k個不同品質的啞鈴組成,優先啞鈴組數最多,然後啞鈴的價格之和最大。

分析:将相同品質的啞鈴統計起來,并且使用vector把不同品質的啞鈴所擁有的價格儲存起來,最後通過不同品質的啞鈴個數是否大于k個,如果不足輸出-1,否則統計前k個數量最多的品質相同的啞鈴中數量最少的一種作為能夠組成的組數,然後将數量大于該組數的啞鈴價值排在前k的統計出來,最後再取和值最大的前k個。

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

int N, K;
int w[4005], c[4005];
int cnt[4005], pos[4005];
int seq[4005];
int idx;
vector<int>v[4005];
// 分别表示重量和價值 

bool cmp(int a, int b) {
    return cnt[a] > cnt[b];
}

bool cmp2(int a, int b) {
    return a > b;
}

void gao(int suit) { // 能夠生産x套啞鈴 
    int ret = 0, n = 0;
    for (int i = 1; i <= 4000; ++i) {
        if (cnt[pos[i]] >= suit) {
            int tmp = 0;
            sort(v[pos[i]].begin(), v[pos[i]].end(), cmp2);
            for (int j = 0; j < suit; ++j) tmp += v[pos[i]][j];
            seq[n++] = tmp;
        }
    }
    sort(seq, seq+n, cmp2);
    for (int i = 0; i < K; ++i) {
        ret += seq[i];
    }
    printf("%d %d\n", suit, ret);
}

int main() {
    while (scanf("%d %d", &N, &K) != EOF) {
        memset(cnt, 0, sizeof (cnt));
        for (int i = 1; i <= 4000; ++i) {
            pos[i] = i;
            v[i].clear();
        }
        for (int i = 0; i < N; ++i) {
            scanf("%d %d", &w[i], &c[i]);
            v[w[i]].push_back(c[i]); // 這個品質的啞鈴後面有這麼些價值的選擇 
            ++cnt[w[i]];
        }
        sort(pos+1, pos+4001, cmp);
        idx = 4000;
        for (int i = 1; i <= 4000; ++i) {
            if (cnt[pos[i]] == 0) {
                idx = i - 1;
                break;
            }
        }
        if (idx < K) {
            puts("0 0");
            continue;
        }
        int suit = 100000;
        for (int i = 1; i <= K; ++i) {
            suit = min(suit, cnt[pos[i]]);
        } // suit表示能夠得到的最多的套數
        gao(suit);
    }
    return 0;
}      

View Code

sgu 550 Tree Queries Online

sgu 551 Preparing Problem

題意:現有N個工作,甲完成這件工作需要t1時間,乙完成這件工作需要t2時間,現在兩人同時開始進行工作,問最後一共完成多少個工作。甲乙開工後不能夠中斷,沒完成一個工作後馬上檢查是否已經有N個工作,如果沒有的話繼續下一次工作。

分析:由于資料範圍較小,是以可以直接進行模拟,維護好兩個時間值,分别表示甲和乙完成下一件工作的時間,如果兩着完成下一次工作的時間相同的話,那麼同時處理,如果不同的話,一個一個進行處理。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int N, a, b;

int main() {
    while (scanf("%d %d %d", &N, &a, &b) != EOF) {
        int ta = a, tb = b, finish = 0, sp;
        while (finish < N) {
            if (ta == tb) {
                finish += 2;
                if (finish < N) ta += a, tb += b;
            } else {
                sp = min(ta, tb);
                if (ta == sp) {
                    ++finish;
                    if (finish < N) ta += a;
                    else ++finish; // b還有一件正在做 
                } else {
                    ++finish;
                    if (finish < N) tb += b;
                    else ++finish; // a還有一件正在做 
                }
            }
        }
        printf("%d %d\n", finish, max(ta, tb));
    }
    return 0;
}       

View Code

sgu 552 Database Optimization

題意:給定一些資料,告訴你每個資料有多少個關鍵字,然後給出這些關鍵字,現在要求給定一種算法使得多關鍵字查詢的過程加快。

分析:由于給定的每條資訊的關鍵字最多隻有4條,是以我們可以直接将每條資訊的關鍵字排序後使用二進制枚舉出所有的組合情況,抽取出關鍵字使用map來存儲,查詢的時候采用同樣的處理方式就可以了。注意:每條關鍵字之前插入一個分隔符防止因為相連導緻的錯誤。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <algorithm>
#include <iostream>
#include <map> 
using namespace std;

map<string,int>mp;

int N, M;
string str[4];

int main() {
    while (cin >> N) {
        int x;
        mp.clear();
        for (int i = 0; i < N; ++i) {
            cin >> x;
            for (int j = 0; j < x; ++j) {
                cin >> str[j];
            }
            sort(str, str+x);
            int mask = 1 << x;
            for (int j = 0; j < mask; ++j) {
                string tmp;
                for (int k = 0; k < 4; ++k) {
                    if (j & (1 << k)) {
                        tmp += " " + str[k];
                    }
                }
                ++mp[tmp];
            }
        }
        cin >> M;
        while (M--) {
            cin >> x;
            for (int i = 0; i < x; ++i) {
                cin >> str[i];
            }
            sort(str, str+x);
            string tmp;
            for (int i = 0; i < x; ++i) {
                tmp += " " + str[i];
            }
            printf("%d\n", mp[tmp]);
        }
    }
    return 0;
}      

View Code

sgu 553 Sultan's Pearls

題意:有N顆珍珠放置在桌子上,其中有M顆懸挂中空中,現在要求每次從首或者是尾拿掉一顆珍珠,每次從尾部拿掉一顆珍珠,那麼就人為的使得珍珠向下滑動一格,使得懸空的珍珠的數量保持不變,現在給定每顆珍珠的價值以及品質,要求最多拿到多少價值的珍珠,并輸出任意一種拿的方案。如果在拿的過程中出現了懸挂在空中的珍珠的總品質大于放置在桌子上的珍珠的總品質之和乘上一個給定的摩擦系數k,那麼宣告該次操作不合法,要求在所有操作均合法的情況下,能夠拿到的最多的價值的珍珠。

分析:此題剛看時以為是動态規劃,但是無奈資料範圍太大,無法開設兩維的狀态來容納下所有的情況,是以需要找到題中的一些特殊的特性。經過觀察可以發現,若最終的結果是拿走了懸空的x顆珍珠,那麼這x顆珍珠一開始就連續的拿是一定能夠拿走的,因為先拿桌子上的珍珠意味着上面的重量減少了。是以此題做法便是枚舉能夠拿走的懸空的珍珠的數量,然後再二分查找能夠能夠從桌子上面拿走的珍珠數量。

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

int N, M, K;

struct Node {
    int w, c;
}e[200005];

int fsum[200005];
int tsum[200005];
int fpri[200005];
int tpri[200005];

bool judge(int x) { // 在未取下左面上的情況下,是否能夠取下懸挂的x個珠子
    if (x >= N-M) return false;
    int u = N-x-M+1, d = N-x;
    if (tsum[u] - tsum[d+1] > K*fsum[u-1]) return false;
    else return true;
}

bool legal(int y, int x) {
    int u = N-x-M+1, d = N-x;
    int l = y+1, r = u - 1;
    if (tsum[u] - tsum[d+1] > K*(fsum[r] - fsum[l-1])) return false;
    else return true;
}

void gao() {
    int ret = -1, x, y; // ret儲存的是能夠獲得的最大價值,x表示取了多少顆懸空的,y表示桌子上取了多少顆 
    for (int i = 0; judge(i); ++i) {
        int l = 0, r = N-i-M-1; // 至少要留一顆珠子在桌子上
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (legal(mid, i)) {
                int tmp = fpri[mid] + tpri[N-i+1];
                if (tmp > ret) {
                    x = i, y = mid;
                    ret = tmp;
                }
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
    }
    printf("%d %d\n", x + y, ret);
    for (int i = 0; i < x; ++i) putchar('H');
    for (int i = 0; i < y; ++i) putchar('T');
    puts("");
}

int main() {
    while (scanf("%d %d %d", &N, &M, &K) != EOF) {
        for (int i = 1; i <= N; ++i) {
            scanf("%d %d", &e[i].w, &e[i].c);
        }
        tsum[N+1] = tpri[N+1] = 0;
        for (int i = 1, j = N; i <= N; ++i, --j) {
            fsum[i] = fsum[i-1] + e[i].w;
            fpri[i] = fpri[i-1] + e[i].c;
            tsum[j] = tsum[j+1] + e[j].w;
            tpri[j] = tpri[j+1] + e[j].c;
        }
        gao();
    }
    return 0;
}       

View Code