牛客練習賽64 A-D題解

A.怪盜-1412

題目地址

A.怪盜-1412

題意簡述

一個長度爲n+m+k包含n個數字1,m個數字2和k個數字4的數組,最多可能有多少個1412子序列?

題解

由於2和4在1412中出現的次數都是1次,所以不需要考慮將連續的4或者是連續的2分開的情況

就比如
1444412
沒必要寫成
4414412
很明顯會出現4的浪費
因爲子序列的相對位置是不能改變的

那麼我們實際上需要考慮的就是1的情況,

設開頭的1有xx個,在4和2之間的1就會有nxn-x
最多子序列即求解(nx)x(n - x) * x
即相當於求解二次函數最大值
y=(nn)/4y = (n * n) / 4

所以序列的組成是這樣的
x個1 k個4 n-x個1 m個2
第一個1我們可以從第一部分取C1x,x箇中取一個作爲第一個
第二個4我們從第二個部分取C1K,往後同理
於是最後的結果
ans=(n * n)/4 * k*m

代碼

int main(){
    int t; RD(t);
    while(t--){
        LL n, m, k; RDD(n, m, k);
        if (n == 1) {
            printf("0\n");
        }
        else {
            LL ans = m * ((n * n) / 4) * k;
            OT(ans);
        }
    }
}

B. Dis2

題目地址

B.Dis2

題意簡述

給你樹的連接,要你求解每個節點所連接的距離爲2的節點有多少個?

  • 樣例輸入
    4
    1 2
    2 3
    3 4

  • 樣例輸出
    1
    1
    1
    1

點{1,3}的距離爲2,點{2,4}的距離爲2。

在這裏插入圖片描述

題解

每個遍歷每個節點(1)所連接的節點(2),通過節點(2)的大小能很清楚計算出與節點(1)距離爲2的點有多少個,但要注意節點(1)連接着節點(2),同時節點(2)也連接着節點(1),所以在計算節點(2)的大小的時候需要注意減1

代碼

typedef vector<int> VI;
const int maxn = 2e6 + 50;
VI G[maxn];
int main(){
    int n; RD(n);
    int u, v;
    REP(i, n - 1){
        RD(u, v);
        G[u].PB(v);
        G[v].PB(u);
    }
    FOR_1(i, 1, n){
        LL ans = 0;
        for(int v: G[i]){
            ans += G[v].size() - 1;
        }
        cout << ans << '\n';
    }
}

C.序列卷積之和

題目地址

C.序列卷積之和

題意簡述

求解 l=1nr=lni=lrj=iraiaj\sum_{l=1}^{n}\sum_{r=l}^{n}\sum_{i=l}^{r}\sum_{j=i}^{r}a_i*a_jmod 1e9+71e9+7

題解

預處理前綴和統計前綴和出現的數量。

n = 2
a[1] * a[1] + a[1] * a[1] + a[1] * a[2] + a[2] * a[2] + a[2] * a[2]
n = 3
a[1] * a[1] + a[1] * a[1] + a[1] * a[2] + a[2] * a[2] + a[1] * a[1] + a[1] * a[2] + a[1] * a[3] + a[2] * a[2] + a[2] * a[3] + a[3] * a[3] + a[2] * a[2] + a[2] * a[2] + a[2] * a[3] + a[3] * a[3] + a[3] * a[3]

//附打印程序
int main(){
    int n; cin >> n;
    for(int l = 1; l <= n; l++) {
        for(int r = l; r <= n; r++) {
            for(int i = l; i <= r; i++) {
                for(int j = i; j <= r; j++){
                    cout << "a" << "[" << i << "]" << " * " << "a" << "[" << j << "]" << " + ";
                }
            }
        }
    }
}

可以很清楚的看到n = 2和 n = 3有那麼一些相似之處
n = 2有的n = 3都有
在n=2中ai2a_i^2出現都是兩次
類推,
易知aia_i只與ii存在關係,同理aja_j只與jj存在關係,所以可以分開考慮
i=lrj=lraiaj=(i=lrai)(j=lraj)\sum_{i=l}^r\sum_{j=l}^ra_i * a_j = (\sum_{i=l}^ra_i)*(\sum_{j=l}^ra_j)

再看一眼n=2的時候的情況
a[1] * a[1] + a[1] * a[1] + a[1] * a[2] + a[2] * a[2] + a[2] * a[2]
其實可以化簡成
2a[1]2+a[1]a[2]+2a[2]22*a[1]^2+a[1]*a[2]+2*a[2]^2
以此去想
(i=lrai)(j=lraj)=((i=lrai)2i=lraiai)/2+i=lraiai(\sum_{i=l}^ra_i)*(\sum_{j=l}^ra_j) = ((\sum_{i=l}^ra_i)^2-\sum_{i=l}^ra_i * a_i)/2+\sum_{i=l}^ra_i * a_i
外層還有兩個循環,類似推斷

這裏是引用
在這裏插入圖片描述
來源於官方題解 https://ac.nowcoder.com/discuss/430962

代碼

const int maxn = 2e5+60;
LL a[maxn], s[maxn], s2[maxn],ans = 0;
const int mod = 1e9 + 7;
int main(){
    int n; RD(n);
    FOR_1(i, 1, n) cin >> a[i];
    FOR_1(i, 1, n) s[i]  = (s[i - 1] + a[i]) % mod;
    FOR_1(i, 1, n) s2[i] = (s2[i - 1] + s[i]) % mod;
    FOR_1(i, 1, n)
    {
        ans += i * a[i] % mod * ((s2[n] - s2[i-1] + mod) % mod - (n - i + 1) * s[i-1] % mod + mod) % mod;
        ans %= mod;
    }
    cout << ans << endl;
}

D.寶石裝箱

題目地址

D.寶石裝箱

題意簡述

nn顆寶石裝進nn個箱子使得每個箱子中都有一顆寶石。第ii顆寶石不能裝入第aia_i個箱子。求合法的裝箱方案對998244353998244353取模。
(兩種裝箱方案不同當且僅當兩種方案中存在一顆編號相同的寶石裝在不同編號的箱子中。)

  • 輸入樣例
    2
    1 2
  • 輸出樣例
    1

題解

容斥+dp

  • 在不考慮不符合情況時,總的方案數allnum
    allnum=n!=n(n1)(n2)...(1)allnum = n! = n * (n - 1) * (n - 2) * ... * ( 1 )
  • 合法方案數實際上就是等於總的方案數 減去 不合法方案數
    ans=allnumunnumans = allnum - unnum
  • 設函數f(x)f(x)表示x箱子中不合法的方案數
  • dp[i][j]dp[i][j]表示前ii個箱子中,存在j個不合法的方案數
  • 這裏運用dp,要注意,你所計算的是方案數,而不是揹包問題中的重量或者是權重什麼的

dp[i][j]=dp[i1][j]+dp[i1][j1]a[i]dp[i][j]=dp[i-1][j]+dp[i-1][j-1] * a[i]

在前i個箱子中有j個不合法方案數,可能是由於前i-1個箱子中就有j個不合法方案數,也有可能是第i個箱子纔有j個不合法方案數,所以是

dp[i1][j]+dp[i1][j1]a[i]dp[i-1][j]+dp[i-1][j-1] * a[i]

至少i個箱子裝了不合法的寶石的方案數 =dp[n][i](ni)!= dp[n][i] * (n - i)!

在這裏插入圖片描述
根據容斥,可得
n!i=1n(1)idp[n][i](ni)!n!-\sum_{i=1}^n*(-1)^i*dp[n][i]*(n-i)!
i=0n(1)idp[n][i](ni)!\sum_{i=0}^n*(-1)^i*dp[n][i]*(n-i)!

代碼

const int maxn = 1e4+50;
LL dp[maxn], fact[maxn];
LL a[maxn];
const LL mod = 998244353;
void init(){
    memset(a, 0, sizeof a);
    fact[0] = 1;
    FOR_1(i, 1, maxn - 1){
            fact[i] = fact[i - 1] * i % mod;
    }
}
int main(){
    init();
    int n; cin >> n;
    FOR_1(i, 1, n){
        LL x; RDD(x); a[x]++;;
    }
    dp[0] = 1;
    FOR_1(i, 1, n){
        for(int j = i; j >= 1; j--){
            (dp[j] += dp[j - 1] * a[i]) %= mod;
        }
    }
    LL ans = 0;
    dp[0] = 1;
    FOR_1( i, 0, n){
        if (i & 1) ans -= dp[i] * fact[n - i];
        else ans += dp[i] * fact[n - i];
        ans = (ans + mod) % mod;
    }
    ans %= mod;
    cout << ans << '\n';
}

case通過率爲%13.3可以考慮一下是不是爆int的問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章