HGOI7.20集训题解

题解

又是lzw大佬出题…不过这次的题好像他也自己以前做过。感觉上并不是特别难但….只是感觉上orz

第一题——游戏(game)

【题目描述】

  • 两个人玩游戏,A每次做任务有 1 2 p 的概率得到 2 p 1 分,B每次只会做难度为1的任务,得分为n时即是获胜。求A获胜的概率。

  • 这个概率题真的是….orz我盯着样例看了半天没看懂什么意思…最后只交了个样例,还好数据有点良心,骗了十分。
  • 正解是利用dp方程地推+记忆化搜索。
  • 由于要得到n分,那干脆直接反过来想。两个人初始有n分,A先到0分的概率是多少。那就可以进行枚举。
  • 假设A有p的概率获得k分,则有1-p的概率不得分。那方程就可以写成:

f [ x ] [ y ] = p f [ x k ] [ y ] + ( 1 p ) f [ x ] [ y ]

  • 由于B的得分也是有概率的那么就需要进行对B的分类了。重新写下方程就是

f [ x ] [ y ] = p ( f [ x k ] [ y ] 2 + f [ x k ] [ y 1 ] 2 ) + ( 1 p ) ( f [ x ] [ y ] 2 + f [ x ] [ y 1 ] 2 )

  • 调整下顺序:

( 1 1 p 2 ) f [ x ] [ y ] = p 2 ( f [ x k ] [ y ] + f [ x k ] [ y 1 ] ) + 1 p 2 ( f [ x ] [ y 1 ] )

  • 左右同乘2

( p 1 ) f [ x ] [ y ] = p ( f [ x k ] [ y ] + f [ x k ] [ y 1 ] ) + ( 1 p ) f [ x ] [ y 1 ]

然后接着去枚举p和k就好了…(记得是p=k<<1)

  • 但是好像直接枚举p有点不现实啊…那就把它转化下变成:

f [ x ] [ y ] = f [ x k ] [ y 1 ] + f [ x k ] [ y ] + ( p 1 ) f [ x ] [ y 1 ] 1 + p


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
void fff(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
}
int n;
double f[1001][1001];
double dp(int x,int y){
    if(x<=0) return 1.0;
    if(y<=0) return 0.0;
    if(f[x][y]>=0){
        return f[x][y];
    }
    for (int k=1,p=2;k<=1024;p<<=1,k<<=1){
        double temp;
        temp=dp(x-k,y-1)+dp(x-k,y)+(double)(p-1)*dp(x,y-1);
        temp/=(double)(p+1);
        f[x][y]=max(f[x][y],temp);
    }
    return f[x][y];
}
int main(){
    fff();
    scanf("%d",&n);
    for (int i=0;i<=1000;i++){
        for (int j=0;j<=1000;j++)
            f[i][j]=-1000;
    }
    printf("%.6lf",dp(n,n));
}

第二题——牛的杂技(acrobat)

【题目描述】

  • n头牛叠罗汉,各自有重量和力量 w i , s i ,定义每头牛的压扁指数是在他之上的所有牛的质量之和-他的力量。压扁指数就是被压得最扁的那头奶牛的压扁指数。现求尽量小的压扁指数。

  • 这个一看就知道是排序ex之类的问题了orz。小小证明下:
  • 对于相邻两头牛有 ( A i , B i ) ( A i + 1 , B i + 1 ) ,在他们之上的重量和为 S
  • 那么交换前和交换之后的压扁指数为:

m a x ( S B i , S + A i B i + 1 ) m a x ( S B i + 1 , S + A i + 1 B i )

  • 现在假设交换前比交换之后的解更优则:

m a x ( S B i , S + A i B i + 1 ) > m a x ( S B i + 1 , S + A i + 1 B i )

  • 左右同时减去 S ,并加上 B i + B i + 1 则:

m a x ( B i + 1 , A i + B i ) > m a x ( B i , A i + 1 B i + 1 )

  • 然后就是常规的排序题了。

  • 但是这个无良数据居然还有负数orz首测只得了60


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
void fff(){
    freopen("acrobat.in","r",stdin);
    freopen("acrobat.out","w",stdout);
}
const int MAXN=51100;
int n;
struct node{
    LL w,s;
    LL sum;
    bool operator < (const node x) const{
        if(sum==x.sum) return s<x.s;
        return sum<x.sum;
    }
}a[MAXN];
int main(){
//  fff();
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%lld%lld",&a[i].w,&a[i].s);
        a[i].sum=a[i].w+a[i].s;
    }
    sort(a+1,a+n+1);
    LL s=0,ans=-999999999;
    for (int i=1;i<=n;i++){
        ans=max(ans,s-a[i].s);
        s+=a[i].w*1ll;
    }
    printf("%lld",ans);
}

第三题——过路费(cost)

【题目描述】

  • 给出n个点和m条双向边。定义两点之间的花费价值为路径上的最长路径长度。现给出q个查询,求出x到y的花费价值。

  • 真的,看到这里你会觉得第一题真**难。看这个题就知道是最小生成树+LCA的板子题。
  • 看我五分钟给你打完LCA!不就是个最小生成树么!

这里写图片描述

  • 但是!我LCA少了个弹出orz。今日爆零…

  • 没什么好讲的吧其实..两点之间存在短的路径就走短的路径,克鲁斯卡尔算法解决。

  • 两点间的查询由倍增LCA来解决。无非是把倍增的比较改成 m a x 而不是相加orz。
  • 就这么简单了..接下来就看你基本功了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
void fff(){
    freopen("cost.in","r",stdin);
    freopen("cost.out","w",stdout);
}
const int MAXN=10010;
int n,m;
struct Edge{
    int from,to,w;
    bool operator <(const Edge x) const{
        return w<x.w;
    }
}edge[MAXN*21];
vector <Edge> edge1;
vector <int> G[MAXN];
int fa[MAXN];
int find_(int x){
    if(x!=fa[x]) fa[x]=find_(fa[x]);
    return fa[x];
}
void merge(int x,int y){
    x=find_(x);
    y=find_(y);
    fa[y]=x;
}
void K(){
    for (int i=1;i<=n;i++) fa[i]=i;
    sort(edge+1,edge+m+1);
    for (int i=1;i<=m;i++){
        Edge e=edge[i];
        int x=find_(e.from),y=find_(e.to);
        if(x==y) continue;
        merge(x,y);
        edge1.push_back((Edge){e.from,e.to,e.w});
        G[e.from].push_back(edge1.size()-1);
        edge1.push_back((Edge){e.to,e.from,e.w});
        G[e.to].push_back(edge1.size()-1);
    }
}
bool visited[MAXN];
int father[MAXN][20],dist[MAXN][20],depth[MAXN];
void dfs(int u,int f,int deepth){
    depth[u]=deepth;
    father[u][0]=f;
    visited[u]=true;
    int siz=G[u].size();
    for (int i=0;i<siz;i++){
        Edge &e=edge1[G[u][i]];
        int v=e.to;
        if(!visited[v]){
            dist[e.to][0]=e.w;
            dfs(e.to,u,deepth+1);
        }
    }
}
void st_set(){
    for (int j=1;j<=19;j++){
        for (int i=1;i<=n;i++){
            father[i][j]=father[father[i][j-1]][j-1];
            dist[i][j]=max(dist[i][j-1],dist[father[i][j-1]][j-1]);
        }
    }
}
int q;
int Query(int x,int y){
    if(depth[x]<depth[y])swap(x,y);
    int ans=0;
    for (int i=19;i>=0;i--){
        if(depth[father[x][i]]>depth[y]){
            ans=max(ans,dist[x][i]);
            x=father[x][i];
        }
    }
    for (int i=19;i>=0;i--){
        if(father[x][i]!=father[y][i]){
            ans=max(ans,max(dist[x][i],dist[y][i]));
            x=father[x][i];
            y=father[y][i];
        }
    }
    ans=max(ans,max(dist[x][0],dist[y][0]));
    return ans;
}
int main(){
    fff();
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        edge[i].from=x,edge[i].to=y,edge[i].w=w;
    }
    K();
    dfs(1,0,1);
    st_set();
    scanf("%d",&q);
    while (q--){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",Query(x,y));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/81132505