qbxt国庆水题记day2

来源:互联网 发布:windows c盘清理 编辑:程序博客网 时间:2024/05/27 03:27

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 longconst 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;}
原创粉丝点击