1. 程式人生 > >qbxt國慶水題記day2

qbxt國慶水題記day2

qbxt國慶水題記

day2

//100 + 0 + 0 = 100
//除了超水題以外其他的都不會

Problem 1. video

Input file: video.in
Output file: video.out
Time limit: 1s
Memory limit: 256M
pluto 喜歡看片,現在他的硬盤裡有 n 部片,但是由於他還要把妹,所以看片時間有限,他只能挑出
其中的 k 部片來看,他想知道有多少種不同的選片方案。方案數可能很大,答案 mod 1000000007 輸出。
Input
輸入檔案第一行一個整數 N, 表示這個星球上的總人口。
接下來 N 行,每行一個正整數,表示每個居民的姓名。
Output


輸出檔案一行一個整數,表示這個星球的價值。
Example
video.in
6 2
video.out
15
Scoring
• 對於 30% 的資料,n ≤10
• 對於 60% 的資料,n ≤3000
• 對於 100% 的資料,n ≤2 ×105

排列組合
根據公式求階乘,在求下逆元即可

程式碼

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long

const int inf = 1000000007;
int n,m;
ll p,q;

void exgcd(ll a,ll b,ll &d,ll &x,ll &y) {
    if
(!b) {d = a, x = 1, y = 0;return;} exgcd(b,a%b,d,y,x); y -= x * (a / b); } ll inv(ll a, ll b) { ll d,x,y; exgcd(a,b,d,x,y); return (x % b + b) % b; } int main() { freopen("video.in","r",stdin); freopen("video.out","w",stdout); cin>>n>>m; m = min(m, n-m); p = 1
, q = 1; for(int i = 1; i <= m; i++) p = (p * i) % inf; q = inv(p,inf); for(int i = n; i >= n - m + 1; i--) q = (q * i) % inf; cout<<q<<endl; return 0; }

Problem 2. chance

Input file: chance.in
Output file: chance.out
Time limit: 1s
Memory limit: 256M
pluto 去找妹子 ×××約會,然後×××要求和 pluto 玩一個遊戲,pluto 贏了才能獲得和 ×××
約會的機會。遊戲內容為:現在有 N 個袋子 (你可以認為它是哆啦 A 夢的口袋,每個袋裡面放著一些
,所以容量十分大,第 i 個袋裡面放著編號為 Li 到 Ri 的球 (除編號外完全相同),pluto 需要從每個
袋裡面摸出一個球,第 i 個袋子任何一個球被摸到的概率是 1/(Ri −Li + 1),如果 pluto 摸出的球中
有 K% 或以上的球的編號的第一位是 1(比如 11,121,199 的第一位是 1, 而 21,233 第一位就不是 1),那
麼 pluto 就將贏得與 ×××約會的機會。現在 pluto 想知道他能人生中第一次與妹子約會的概率有多大。
Input
第一行兩個整數 N, K
接下來 N 行,每個兩個整數,Li 和 Ri
Output
一行一個實數(保留 7 位⼩數)表示答案
絕對誤差不超過 10−6 即視為正確
Example
chance.in
2 50
1 2
9 11
chance.out
0.833333333333333

Scoring
• 對於 100% 的資料,0 ≤k ≤100,0 < Li ≤Ri
• 對於 30% 的資料,n ≤10,Li ≤Ri ≤100
• 對於 60% 的資料,n ≤500,Li ≤Ri ≤2000
• 對於 100% 的資料,n ≤2000,Li ≤Ri ≤1018

首先判斷出每個袋子裡拿到第一個數字為1的球的概率為pi(很巧妙的判斷見”pd()”)
dp[i][j] 表示前i個袋子裡取到了j個第一個數字為1的球的概率
dp[i][j] = dp[i - 1][j - 1] * pi + dp[i - 1][j] * (1 - pi)
然後根據K%求出至少要多少人,相加即可

//為什麼這麼多dp
//啊啊啊啊啊

程式碼

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

const int maxn = 2000 + 100;
int n;
long long l,r;
double k,pi[maxn],f[maxn][maxn];

long long pd(long long x) {
    long long num = 1,sum = 1;
    if(!x) return 0;
    for(int i = 2; i <= 20; i++) {
        num *= 10;
        if(num > x) return sum;
        if(num * 2 > x) return sum + x - num + 1;
        sum += num;
    }
}

int pp(double p) {
    if(p - (int)p == 0) return (int)p;
    else{
         return (int)(p + 1);
    }
}

int main() {
    freopen("chance.in","r",stdin);
    freopen("chance.out","w",stdout);
    cin>>n>>k;
    for(int i = 1; i <= n; i++) {
        scanf("%lld%lld",&l,&r);
        pi[i] = (double)(pd(r) - pd(l-1)) / (double)(r - l + 1);
    }
    f[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j <= i; j++) {
            f[i][j] += f[i-1][j] * (1 - pi[i]);
            if(j) f[i][j] += f[i-1][j-1] * pi[i];
        }
    }
    k /= 100;
    int t = pp(n*k);
    double ans = 0;
    for(int i = t; i <= n; i++) {
        ans += f[n][i];
    } 
    printf("%.7f\n",ans);
    return 0;
}

Problem 3. plutotree

Input file: plutotree.in
Output file: plutotree.out
Time limit: 2s
Memory limit: 256M
有一棵 n 個節點的樹,節點編號為 1 到 n,i 號節點的權值為 Wi。這棵樹有些奇怪,它的每一個
葉子節點都是根節點的夫親 (表示每個葉子節點與根節點之間有一條邊權為 0 的邊)。我們稱這樣的樹為
pluto 樹,根節點編號為 1。我們需要最小化從 u 到 v 的路徑 (每條邊只能經過一次) 上的節點權值之和,
並且在最小化節點權值之和的同時求這個路徑上可能的最大權值。
Input
第一行兩個整數 n 和 q,n 表示節點個數,q 表示詢問個數。
第二行 n −1 個整數 Ai,表示 i + 1 號節點的夫親為 Ai
第三行 n 個整數 Wi 表示 i 號節點的權值為 Wi
接下來 q 行,每行兩個整數 u,v,表示一組詢問
Output
對於每組詢問輸出兩個整數 x, y
x 表⽰ u 到 v 的權值和最小的路徑的權值和,y 表⽰這條路徑上點權最大值。如果有多個相同權值
和的路徑,輸出那個點權最大值最大的。
Example
plutotree.in
5 1
1 2 3 4
413 127 263 869 960
1 5
plutotree.out
1373 960

Scoring
• 對於 30% 的資料,n ≤300,q ≤1000
• 對於 50% 的資料,n ≤2000,q ≤10000
• 對於 100% 的資料,n ≤100000,q ≤100000

//樹上dp
//不會,暴力也不會打

來自題解
30% 把圖建建出來 floyd
60% 把floyd換成spfa或者dijkstra
100% 樹形dp
首先答案可能有三種情況:
1.不走葉子到根的邊, 那就是一個簡單的樹上的問題, 兩點之間有且僅有一條路徑,樹上倍增一下就可以把兩個問題都解決, 或者閒著蛋疼的可以用樹剖

2.從u走到葉子,從葉子到根,從根到v (這種情況要把u、v調換再做一次)
3.從u走到葉子,從葉子到根,從根到另一個葉子節點,再從葉子節點到v

對於第二種、三種情況, 其實關鍵在於求出每個點到離他最近的葉子的路徑,這樣的路徑有兩種可能,一種是向下下走到葉子, 一種是先向上(有可能經過根也可能不經過)走,最終到達葉子
首先樹形dp出每個節點向下到離它最近的葉子節點的距離,並同時記錄這個路徑上的最大值,設為down[i], (down[i]為二元組,<路徑權值和, 路徑上最大權值>) down[i] 可以從 i號節點的所有兒子轉移過來, 轉移方程很顯然
有了down[i], 就可以dp出我們需要的每個點到最近的葉子節點的路徑了,將這個記為dp[i], dp[i]同樣是一個二元組,和down[i]一樣。 首先dp[1]我們是知道的,就是從根直接走到某一個葉子節點, 其餘的dp[i]初值都是down[i], dp[i]可以從i號節點的父親轉移過來,所以這是一個自上而下的dp

考慮到可能爆棧的情況, 建議使用bfs處理樹形dp

具體狀態如何轉移可以參考std

程式碼std

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
const int N = 111111;
const int inf = 1 << 20;
int fa[N][20], n, m, maximum[N][20], leaf[N], weight[N];
int dist[N],dep[N];
pair<int, int> dp[N], down[N];
vector<int> adj[N];

void update(pair<int, int> &x, pair<int, int> y) {
    if (x.first != y.first) {
        x = min(x, y);
    } else {
        x = max(x, y);
    }
}

void build() {
    vector<int> queue;
    queue.push_back(1);
    fa[1][0] = 0;
    dep[1] = 1;
    dist[1] = weight[1];
    for (int head = 0; head < (int)queue.size(); head++) {
        int now = queue[head];
        for (int i = 0; i < (int)adj[now].size(); i++) {
            queue.push_back(adj[now][i]);
            dep[adj[now][i]] = dep[now] + 1;
            dist[adj[now][i]] = dist[now] + weight[adj[now][i]];
        }
    }

    for (int j = 1; j <= 18; j++) {
        for (int i = 1; i <= n; i++) {
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
            maximum[i][j] = max(maximum[i][j - 1], maximum[fa[i][j - 1]][j - 1]);
        }
    }

    for (int i = (int)queue.size() - 1; i >= 0; i--) {
        int x = queue[i];
        update(down[fa[x][0]], make_pair(down[x].first + weight[fa[x][0]], max(down[x].second, weight[fa[x][0]])));
    }
}

void DP() {
    vector<int> queue;
    queue.push_back(1); 
    update(dp[1], down[1]);
    for (int head = 0; head < (int)queue.size(); head++) {
        int now = queue[head];
        for (int i = 0; i < (int)adj[now].size(); i++) {
            int to = adj[now][i];
            dp[to] = down[to];
            update(dp[to], make_pair(dp[now].first + weight[to], max(weight[to], dp[now].second)));
            queue.push_back(to);
        }
    }
}

pair<int, int> query(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    int fx = x, fy = y;
    int maxd;
    int ret1 = 0, ret2 = 0;
    for (maxd = 0; (1 << maxd) <= dep[x]; maxd++);
    maxd--;
    ret1 = max(maximum[x][0], maximum[y][0]);
    for (int i = maxd; i >= 0; i--) {
        if (dep[fa[x][i]] >= dep[y]) {
            ret1 = max(ret1, maximum[x][i]);
            x = fa[x][i];
        }
    }
    ret1 = max(ret1, maximum[x][0]);
    ret1 = max(ret1, maximum[y][0]);
    if (x == y) {
        ret2 = dist[fx] - dist[fy] + weight[fy]; 
        return make_pair(ret1, ret2);
    }

    for (int i = maxd; i >= 0; i--) {
        if (fa[x][i] != 0 && fa[x][i] != fa[y][i]) {
            ret1 = max(ret1, maximum[x][i + 1]);
            ret1 = max(ret1, maximum[y][i + 1]);
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    ret1 = max(ret1, maximum[x][0]);
    ret1 = max(ret1, maximum[y][0]);
    ret1 = max(ret1, maximum[fa[x][0]][0]);
    ret2 = dist[fx] + dist[fy] - dist[fa[x][0]] * 2 + weight[fa[x][0]];
    return make_pair(ret1, ret2);
}
void add(int x, int y) {
    adj[x].push_back(y);
}
void solve() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) adj[i].clear();
    for (int i = 1; i <= n - 1; i++) {
        int x;
        scanf("%d", &x);
        add(x, i + 1);
        fa[i + 1][0] = x;
    }
    for (int i = 1; i <= n; i++) {
        if (adj[i].size() == 0) leaf[i] = 1;
        else leaf[i] = 0;
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &weight[i]);
        maximum[i][0] = weight[i];
    }

    for (int i = 1; i <= n; i++) {
        dp[i] = make_pair(inf, 0);
        down[i] = make_pair(inf, 0);
    }

    for (int i = 1; i <= n; i++) {
        if (leaf[i]) {
            down[i] = make_pair(weight[i], weight[i]);
            update(dp[1], make_pair(weight[1] + weight[i], max(weight[1], weight[i])));
        }
    }
    build();    
    DP();
    for (int i = 1; i <= m; i++) {
        pair<int, int> tmp, ans;
        int u, v;
        scanf("%d%d", &u, &v);
        tmp = query(u, v);
        ans = make_pair(tmp.second, -tmp.first);
        tmp = make_pair(dp[u].first + dist[v], -max(dp[u].second, maximum[v][18]));
        ans = min(ans, tmp);
        tmp = make_pair(dp[v].first + dist[u], -max(dp[v].second, maximum[u][18]));
        ans = min(ans, tmp);
        tmp = make_pair(dp[u].first + dp[v].first + weight[1], -max(max(dp[u].second, dp[v].second), weight[1]));
        ans = min(ans, tmp);
        printf("%d %d\n", ans.first, -ans.second);
    } 

}

int main() {
    freopen("plutotree.in", "r", stdin);
    freopen("plutotree.out", "w", stdout);

    int tests = 1;
    for (int i = 1; i <= tests; i++) {
        solve();
    }
    return 0;
}

相關推薦

qbxt國慶題記day2

qbxt國慶水題記 day2 //100 + 0 + 0 = 100 //除了超水題以外其他的都不會 Problem 1. video Input file: video.in Output file: video.out Time l

qbxt國慶題記day4

qbxt國慶水題記 day4 //0 + 20 + 0 == 20 //DP(不會) + 暴力 + 找規律(沒找到) beautiful 1.1 題目 從前有一個序列 a[],對於每個 a[i] 都有一個在序列中的優美值,其定義是:

qbxt國慶題記day3

qbxt國慶水題記 day3 //30 + 5 + 30 = 65 //暴力 + 暴力+ 暴力 tree 1.1 題目 從前有一棵樹,編號 1 到 n,確定一個根節點,最大化所有點深度之和 1.2 輸入Ṭ 第一行 n 接下來 n

牛客國慶集訓派對Day2 H 卡牌遊戲 [ 期望dp ]

題目描述 小貝喜歡玩卡牌遊戲。某個遊戲體系中共有N種卡牌,其中M種是稀有的。小貝每次和電腦對決獲勝之後都會有一個抽卡機會,這時系統會隨機從N種卡中選擇一張給小貝。普通卡可能多次出現,而稀有卡牌不會被重複抽到。小貝希望收集到K種稀有卡牌,她想知道期望需要多少次獲勝才能實現這個目標。

牛客國慶集訓派對Day2 H 卡牌遊戲【期望】

連結:https://www.nowcoder.com/acm/contest/202/H 來源:牛客網   時間限制:C/C++ 1秒,其他語言2秒 空間限制:C/C++ 1048576K,其他語言2097152K Special Judge, 64bit IO Format:

牛客國慶集訓派對Day2 F 平衡二叉樹【遞推】

時間限制:C/C++ 1秒,其他語言2秒 空間限制:C/C++ 1048576K,其他語言2097152K 64bit IO Format: %lld 題目描述  平衡二叉樹,顧名思義就是一棵“平衡”的二叉樹。在這道題中,“平衡”的定義為,對於樹中任意一個節點,都滿足左右子樹的

牛客國慶集訓派對Day2 A 矩陣乘法(思維分塊)

題目連結 題意: 給你兩個矩陣A,B, A是n*p,B是p*m,B是一個只有0,1組成的矩陣,Aij<65536 C=A*B,讓你求出C的裡面所有元素的異或和   解析: 官方的標解是分塊,每8個分一組。 例如對於A,每行行每8個分成一組,對於B,

牛客國慶集訓派對Day2: H. travel(樹形線頭DP)

連結:https://ac.nowcoder.com/acm/contest/140/H 來源:牛客網   題目描述 White Cloud has a tree with n nodes.The root is a node with number 1. Each nod

牛客國慶集訓派對Day2 F-平衡二叉樹(簡單dp)

思路來源 錢神 錢神如是說(樣例n=4 d=1): 對於高度為5,滿足d=1的樹來說,只需要左子樹高度4,右子樹高度3 就可以滿足d=1 右子樹只要滿足右左子樹高度3且d=1,右右子樹高度2且d=1 然後遞迴的去求 最後發現其實可以遞推。 題解 轉移方程

牛客國慶集訓派對Day2 F平衡二叉樹(構造規律)

隊友發現在層數到達d之前和d之後的答案規律不同。。。 #include<bits/stdc++.h> using namespace std; #define ll lon

牛客國慶集訓派對Day2 A 矩陣乘法(簡單)

你以為評測機跑的慢嗎 那你就大錯特錯啦! 程式碼是轉的。。。 #include<iostream> #include<cstdio> #include<

牛客國慶集訓派對Day2 H 卡牌遊戲 [ 期望dp ]

題目描述 小貝喜歡玩卡牌遊戲。某個遊戲體系中共有N種卡牌,其中M種是稀有的。小貝每次和電腦對決獲勝之後都會有一個抽卡機會,這時系統會隨機從N種卡中選擇一張給小貝。普通卡可能多次出現,而稀有卡牌不會被重複抽到。小貝希望收集到K種稀有卡牌,她想知道期望需要多少次獲勝

牛客國慶集訓派對Day2 魔法陣

題意: 給出3個點,確定一個正三角形,每個三角形的頂點一一對應一個給出的點,使對應的最大距離最小。 題解: 官方的 程式碼: #include<bits/stdc++.h> #define N 1010 #define INF 0x

牛客國慶集訓派對Day2 E 資料排序(狀態壓縮dp)

雖然說得分可以一樣,但是我們還是可以僅僅只用兩個狀態0和1來表示這一題。 某一個位置為0表示當前這個數字還沒有被標號,也即當前數字比所有的已經標號的數字都要小。如果為1,那麼說明這個已經標號,並且這個數字與比它先標號的數字和與其相等的數字的衝突值已經計算過了

牛客國慶集訓派對Day2 A 矩陣乘法

就是讓你做一個矩陣乘法,但是可能暴力做複雜度比較高(不會告訴你暴力也能過)。 題目已經大致告訴你優化的方法了。如果兩個二進位制向量點乘,那麼可以用與運算代替乘法運算,最後統計結果中1的個數。而本題就是讓你實現這個過程。 可以看到,第二個矩陣是一個01矩陣,

牛客國慶集訓派對Day2 A

題目描述 深度學習演算法很大程度上基於矩陣運算。例如神經網路中的全連線,本質上是一個矩陣乘法;而卷積運算也通常是用矩陣乘法來完成的。有一些科研工作者為了讓神經網路的計算更快捷,提出了二值化網路的方法,就是將網路權重壓縮成只用兩種值表示的形式,這樣就可以用一些 trick 加

牛客國慶集訓派對Day2 平衡二叉樹 DP找規律

#include<bits/stdc++.h> using namespace std; const int MAX=61; int n,d; long long dp[MAX]; int main() { scanf("%d%d",&n,&a

牛客國慶集訓派對Day2

題意: 給你兩個矩陣,做乘法之後,算出所有元素異或後的答案。 POINT: 把N x P, P x M的兩個矩陣,都對p分為每8個一份。 對n行的p個數,每8個數一組,把所有選取的狀態

牛客國慶集訓派對Day2 F 平衡二叉樹

時間限制:C/C++ 1秒,其他語言2秒 空間限制:C/C++ 1048576K,其他語言2097152K 64bit IO Format: %lld 題目描述 平衡二叉樹,顧名思義就是一棵“平衡”的二叉樹。在這道題中,“平衡”的定義為,對於樹中任意一個節點,都滿足左

泛刷題記17.12.27

SHOI的題目質量好高啊,感覺都是些不錯的題目……(我都不會做) LOJ 2142「SHOI2017」相逢是問候 題意:給定一個序列,在模p意義下,茲瓷區間對給出的一個常數c取冪(原來是ai,變成cai),以及區間求和的操作,注意p不一定是質數,