DAY 5模擬賽

DAY 5

廖俊豪神仙出題


T1

最小差別矩陣(a.cpp, a.in, a.out)

【題目描述】

有一個 n*m 的矩陣,矩陣的每一個位置上能夠放置一個數。對於第 i 行,第 i 行的差別定義爲該行的最大數和最小數的差。一個矩陣的差別,定義爲矩陣中每一行差別的最大值。如今給定 k 個數 v[1..k],問:從這 k 個數中選 n*m 個數放入矩陣,可以獲得的矩陣的差別最小值是多少。c++

【輸入格式】

第一行三個整數,k, n, m,表示有 k 個數可選,矩陣的行數和列數分別爲 n 和 m。優化

第二行 k 個整數,表示備選的數 v[1..k]。spa

【輸出格式】

輸出一個數,表示可以獲得的最小差別值3d

【樣例輸入】

5 2 2code

7 5 8 2 3blog

【樣例輸出】

1排序

【數據範圍與提示】

對於 30%的數據,k <= 10, n <= 3, m <= 3ci

對於 100%的數據,n * m <= k <= 100000, n, m <= 1000,0 <= v[i] <= 10^9it

256MB,1sclass

 

題解

先排序

二分差別的最大值k,而後貪心

放數必定是連續的,將某一段放到一行

從前日後找,若是說從某個數開始日後數m個數,最大值減去最小值小於等於枚舉的k就合法,那麼就把這m個數放到同一行,最後判斷是否可以湊出n行

O(klogC)

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 7;
int n, m, k, v[maxn];

int judge(int d)
{
    int tmp = 0;
    for (int i=1; i+m-1<=k; ++i)
    {
        if (v[i+m-1] - v[i] <=d)
            ++tmp, i += m - 1;
    }
    if (tmp >= n) return 1;
    return 0;
}

int main()
{
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    scanf("%d%d%d", &k, &n, &m);
    for (int i=1; i<=k; ++i)
        scanf("%d", &v[i]);
    sort(v + 1, v + k + 1);
    int left = 0, right = 1e9;
    while (left < right)
    {
        int mid = (left + right) / 2;
        if (judge(mid))
            right = mid;
        else
            left = mid + 1;
    }
    printf("%d\n", left);
    return 0;
}

 

T2

分割序列(b.cpp, b.in, b.out)

【題目描述】

給定一個長度爲 n 的序列 v[1..n],如今要將這個序列分紅 k 段(每段都不能爲空),定義每一段的權值爲該段上的全部數的或和。定義整個序列的權值爲每段權值的和。問:這個序列的最大權值爲多少。

【輸入格式】

第一行兩個數 n 和 k,意義如題意所示。

第二行 n 個數,表示這個序列 v[1..n]。

【輸出格式】

輸出一個數,表明這個序列的最大權值。

【輸入樣例】

5 2

7 5 8 2 3

【輸出樣例】

22

【數據範圍與時空限制】

對於 30%的數據,n <= 10, k <= 10

對於 60%的數據,n <= 100, k <= 100

對於 100%的數據,k <= n <= 2000,1 <= v[i] <= 5 * 10^5

256MB,1s

 

題解

f[i][j]表示前i個數分j段的最大權值和  ans=f[n][k]

能夠先預處理出每一段的或和sum[i][j]

轉移:f[i][j]=max(f[l][j-1]+sum[l+1][i])(l<i) l表示將第i+1到第i個數當成一段

                                               

 

60pts

怎麼優化?

仍是先n^2預處理

找單調性  f[l][j-1]隨着l的增長遞增  sum[l+1][i]隨着l的增長而遞減

Sum是按位或:好比變化到1e6,最多變化20次

 

                                                    

 

若是按sum值分段,那麼最優的必定是某一段sum的最後加上f的值

那麼對於每個j,咱們能夠枚舉它前面的點i,把sum分段

If(sum[i][j]!=sum[i+1][j])  vector[j].push_back(i);

在剛纔枚舉l的時候就能夠變成枚舉決策點了,這一維變成了log的複雜度

                                                         

 

 

O(n*k*logC)

#include <bits/stdc++.h>
using namespace std;

const long long inf = 1ull << 30 << 20;
const int maxn = 2005;
vector<int> point[maxn];
int n, K, v[maxn], sum[maxn][maxn];
long long f[maxn][maxn];
int main()
{
    freopen("b.in", "r", stdin);
    freopen("b.out", "w", stdout);
    cin >> n >> K;
    for (int i=1; i<=n; ++i) cin >> v[i];
    for (int i=1; i<=n; ++i) sum[i][i] = v[i];
    for (int i=1; i<n; ++i)
        for (int j=i+1; j<=n; ++j)
            sum[i][j] = sum[i][j-1] | v[j];
    for (int i=1; i<=n; ++i)
    {
        point[i].push_back(i);
        for (int j=i-1; j>=1; --j)
            if (sum[j][i] != sum[j+1][i])
                point[i].push_back(j);
    }
    for (int k=1; k<=K; ++k)
        for (int i=1; i<=n; ++i)
        {
            if (i < k) continue;
            for (int j=0; j<point[i].size(); ++j)
            {
                int x = point[i][j];
                if (x >= k)
                    f[i][k] = max(f[i][k], f[x-1][k-1] + sum[x][i]);
            }
        }
    cout << f[n][K] << endl;
    return 0;
}

 


 

 

T3

樹的魔法值(C.cpp, C.in, C.out)

【題目描述】

有一棵 k+1 層的滿二叉樹,那麼該樹有 2^k 個葉子節點。給定 n 個機器人(n=2^k),編號從 1—n,編號爲 i 的機器人的權值爲 v[i]。咱們如今要將這 n 個機器人分別放置在這 n 個葉子節點上,每一個葉子節點放且只能放一個機器人。葉子節點的權值爲該葉子節點上的機器人的權值。非葉子節點的權值定義爲該樹中編號最大的機器人的權值。每一個非葉子節點除了權值之外,還有一個魔法值,該點的魔法值爲其左右兒子節點的權值的乘積。整棵樹的魔法值定義爲非葉子節點的魔法值的和。

問:將這 n 個機器人隨機地放在這 n 個葉子節點上,樹的魔法值的指望爲多少。

【輸入格式】

第一行爲一個整數 k,含義如題所示。

第二行爲 2^k 個整數,依次表示這 n 個機器人的權值。

【輸出格式】

假設答案爲一個不可約分數 P/Q,則輸出在模 1e9+7 意義下的 P * (Q^-1)模 1e9+7 的值。

【樣例輸入 1】

2

1 3 5 7

【樣例輸出 1】

59

【樣例解釋】

對於 n=4 的狀況,機器人共有 24 種不一樣的安放方案。其中,本質不一樣的有 3 種,分別是((1,3),(5,7)), ((1,5),(3,7)), ((1,7),(5,3)),魔法值分別爲 1*3+5*7+3*7=59, 1*5+3*7+5*7=61,1*7+5*3+5*7=57, 答案爲(57+59+61)/3 = 59。

【樣例輸入 2】

2

1 5 3 7

【樣例輸出 2】

333333390

【數據範圍與時空限制】

30%的數據,k <= 3

60%的數據,k <= 10

100%的數據,k <= 18

256MB,1s

 

題解:

30pts  k<=3 --> n<=8  n!暴力  指望:全部狀況的值加起來取平均  就是除以n!

60pts  k<=10,n<=1024  求出全部狀況的魔法值的和?

考慮兩個點x和y在第d層對答案的貢獻

分步考慮  考慮兩個點在第d層相遇,那麼應該算清楚第d層相遇了多少次,統計出來就能夠算出貢獻

Σ(v[x]*v[y]*times[x][y])

怎麼算?

設x>y

在y的子樹裏面,必定有2^d-1個小於y的

                                                      

 

 

1)在y-1個數裏面,選擇2^d -1個小於y的方案數C(y -1, 2^y -1)

2)在x- 2^d -1個數裏面,選擇2^d -1個小於x的方案數 C(x- 2^d -1 , 2^d-1)

3) 其餘n- 2^(d+1) 任意排列n- 2^(d+1)!

4) 考慮x和y相遇的位置  2^(k-d)

5) 上面的都不對,由於要求排列,因此是A

6) x和y的位置能夠互換  *2

  x>2^(d+1)  y>=2^d  x>y

bits[i]表示2^i

c[i][j]表示C(i,j)

fac[i]表示i!

  枚舉x,y,d

                                                       

 

 

O(n^2logn)

 

100pts

優化式子自己

                                                     

 

                                                     

 

發現最後一部分只在枚舉x的時候和y有關,因此能夠把它放到外面去

預處理出後綴和,枚舉d和y就好了

 

#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;
const int maxn = (1 << 18) + 7;
typedef long long LL;

LL fac[maxn], inv_fac[maxn], bit[maxn], sum[maxn];
int n, v[maxn], k;
int powmod(int x, int times)
{
    LL tmp = 1;
    while (times > 0)
    {
        if (times & 1) tmp = tmp * x % mod;
        x = (LL)x * x % mod;
        times >>= 1;
    }
    return tmp;
}

LL C(int x, int y)
{
    if (x < y) return 0;
    return fac[x] * inv_fac[y] % mod * inv_fac[x-y] % mod;
}

int main()
{
    freopen("c.in", "r", stdin);
    freopen("c.out", "w", stdout);
    fac[0] = 1; for (int i=1; i<maxn; ++i) fac[i] = fac[i-1] * i % mod;
    inv_fac[maxn-1] = powmod(fac[maxn-1], mod - 2); 
    for (int i=maxn-2; i>=0; --i) inv_fac[i] = inv_fac[i+1] * (i + 1) % mod;
    bit[0] = 1; for (int i=1; i<=20; ++i) bit[i] = bit[i-1] * 2;
    
    scanf("%d", &k);
    n = bit[k];
    for (int i=1; i<=bit[k]; ++i) scanf("%d", &v[i]);
    //for (int i=bit[k]; i>=1; --i) scanf("%d", &v[i]);
    LL ans = 0;
    for (int d=1; d<=k; ++d)
    {
        LL tmp = 0;
        for (int i=n; i>=bit[d]; --i)
            sum[i] = (sum[i+1] + C(i-1-bit[d-1], bit[d-1]-1) * v[i]) % mod;
        for (int j=n-1; j>=bit[d-1]; --j)
            tmp = (tmp + C(j-1, bit[d-1]-1) * v[j] % mod * sum[max(bit[d], (LL)j + 1)]) % mod;
        ans = (ans + tmp * fac[bit[d-1]] % mod * fac[bit[d-1]] % mod * fac[n-bit[d]] % mod 
                * 2 % mod * bit[k-d]) % mod;
    }
    ans = ans * inv_fac[n] % mod;
    cout << ans << endl;
    return 0;
}
    
    
相關文章
相關標籤/搜索