1. 程式人生 > >P4365 [九省聯考2018]秘密襲擊coat

P4365 [九省聯考2018]秘密襲擊coat

一場 upload 任務 http pre 轉化 add 集合 mod

$ \color{#0066ff}{ 題目描述 }$

Access Globe 最近正在玩一款戰略遊戲。在遊戲中,他操控的角色是一名C 國士 兵。他的任務就是服從指揮官的指令參加戰鬥,並在戰鬥中取勝。

C 國即將向D 國發動一場秘密襲擊。作戰計劃是這樣的:選擇D 國的s 個城市, 派出C 國戰績最高的s 個士兵分別秘密潛入這些城市。每個城市都有一個危險程度did_idi,

C 國指揮官會派遣戰績最高的士兵潛入所選擇的城市中危險程度最高的城市,派遣戰績第二高的士兵潛入所選擇的城市中危險程度次高的城市,以此類推(即派遣戰績第i高的士兵潛入所選擇城市中危險程度第i 高的城市)。D 國有n 個城市,n - 1 條雙向道路連接著這些城市,使得這些城市兩兩之間都可以互相到達。為了任務執行順利,C 國選出的s 個城市中,任意兩個所選的城市,都可以不經過未被選擇的城市互相到達。

Access Globe 操控的士兵的戰績是第k 高,他希望能估計出最終自己潛入的城市的 危險程度。Access Globe 假設C 國是以等概率選出任意滿足條件的城市集合S ,他希望你幫他求出所有可能的城市集合中,Access Globe 操控的士兵潛入城市的危險程度之和。如果選擇的城市不足k 個,那麽Access Globe 不會被派出,這種情況下危險程度為0。

當然,你並不想幫他解決這個問題,你也不打算告訴他這個值除以998 244 353 的 余數,你只打算告訴他這個值除以64,123 的余數。

\(\color{#0066ff}{輸入格式}\)

從文件coat.in 中讀入數據。

第1 行包含3 個整數n、k、W,表示D 國城市的個數、Access Globe 所操控士兵 潛入的城市戰績排名以及D 國的所有城市中最大的危險程度;

第2 行包含n 個1 到W 之間的整數\(d_1\); \(d_2\); ... \(d_n\),表示每個城市的危險程度;

第3 行到第n + 1 行,每行兩個整數\(x_i\); \(y_i\),表示D 國存在一條連接城市\(x_i\) 和城市\(y_i\) 的雙向道路。

\(\color{#0066ff}{輸出格式}\)

輸出到文件coat.out 中。
輸出一個整數,表示所有可行的城市集合中,Access Globe 操控的士兵潛入城市的危險程度之和除以64,123 的余數。

\(\color{#0066ff}{輸入樣例}\)

5 3 3
2 1 1 2 3
1 2
2 3
1 4
1 5
   
    
10 2 3
2 1 1 3 1 2 3 3 1 3
1 2
2 3
2 4
2 5
2 6
5 7
1 8
8 9
1 10

\(\color{#0066ff}{輸出樣例}\)

11
   
435

\(\color{#0066ff}{數據範圍與提示}\)

D 國地圖如下,其中危險程度為d 的城市的形狀是(d + 3) 邊形。

技術分享圖片

以下是所有符合條件且選擇的城市不少於3 個的方案:

? 選擇城市1、2、3,Access Globe 的士兵潛入的城市危險程度為1;

? 選擇城市1、2、3、4,Access Globe 的士兵潛入的城市危險程度為1;

? 選擇城市1、2、3、5,Access Globe 的士兵潛入的城市危險程度為1;

? 選擇城市1、2、3、4、5,Access Globe 的士兵潛入的城市危險程度為2;

? 選擇城市1、2、4,Access Globe 的士兵潛入的城市危險程度為1;

? 選擇城市1、2、5,Access Globe 的士兵潛入的城市危險程度為1;

? 選擇城市1、2、4、5,Access Globe 的士兵潛入的城市危險程度為2;

? 選擇城市1、4、5,Access Globe 的士兵潛入的城市危險程度為2;而在選擇的 城市少於3 時,Access Globe 的士兵潛入的城市危險程度均為0;

所以你應該輸出(1 + 1 + 1 + 2 + 1 + 1 + 2 + 2) mod 64 123 = 11。

技術分享圖片

7000ms / 1024MB

\(\color{#0066ff}{題解}\)

考慮正解暴力碾標算

這種數據範圍,可以想到樹形DP

答案就是所有聯通塊的第k大之和

對於每個聯通塊求第k大是不太好弄的

可以轉化一下

我們枚舉一個下界x,\(ans+=第k大\ge x的聯通塊個數\)

這樣的話,比如第k大是5,那麽我們從1枚舉到5,每次都會算一遍,對答案的貢獻是正確的

然後就可以DP了

\(f[i][j]為以i為根子樹選j個\ge x的點且與i聯通的聯通塊個數\)

跑樹形背包即可

上界\(O(n^3)\)

卡卡常數就能碾過!

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 2050;
const int mod = 64123;
struct node {
    int to;
    node *nxt;
    node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
};
node *head[maxn];
int f[maxn][maxn], n, k, w, s[maxn], val[maxn], ans, siz[maxn];
void add(int from, int to) {
    head[from] = new node(to, head[from]);
}
void dfs(int x, int fa, int limit) {
    f[x][siz[x] = val[x] >= limit] = 1;
    for(node *i = head[x]; i; i = i->nxt) {
        if(i->to == fa) continue;
        dfs(i->to, x, limit);
        for(int l = siz[x]; l >= 0; l--) {
            if(f[x][l]) {
                for(int r = siz[i->to]; r >= 0; r--) {
                    if(f[i->to][r])
                        (f[x][l + r] += 1LL * f[x][l] * f[i->to][r] % mod) %= mod;
                }
            }
        }
        siz[x] += siz[i->to];
    }
    for(int i = k; i <= siz[x]; i++) (ans += f[x][i]) %= mod;
}



int main() {
    n = in(), k = in(), w = in();
    for(int i = 1; i <= n; i++) s[val[i] = in()]++;
    int x, y;
    for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y), add(y, x);
    for(int i = w; i >= 1; i--) s[i] += s[i + 1];
    for(int now = 1; now <= w; now++) {
        if(s[now] < k) break;
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= n; j++) 
                f[i][j] = 0;
        dfs(1, 0, now);
    }
    printf("%d\n", ans);
    return 0;
}

P4365 [九省聯考2018]秘密襲擊coat