HGOI7.15集训题解

题解

为什么只有7.15的题解没有14号的呢..因为14号难得放天假啊..为什么要把这么美好的日子拿来做题呢!当然应该出去走走拍拍风景和好看的小姐姐了。今天的题比较水,做的事14年的真题(当年好像一等线500?)首测275.比较一般。


第一题——生活大爆炸版石头剪刀布(rps)

【题目描述】

  • 告诉你两个人在玩石头剪刀布,并引入了两种新的方式。然后给你张胜负表,告诉你赢得得一分,平局和输的不得分。每人出拳有一个固定周期,一共进行n轮。求最终两个人的得分。

这里写图片描述

  • 表中的输入顺序分别用12345表示。

  • 这个真的是水题。先根据两个人的出拳建立表(5X5),然后再线性模拟就好了。算法复杂度是 O ( n )
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
void fff(){
    freopen("rps.in","r",stdin);
    freopen("rps.out","w",stdout);
}
int mp[5][5]={
    0,0,1,1,0,
    1,0,0,1,0,
    0,1,0,0,1,
    0,0,1,0,1,
    1,1,0,0,0
};
int n,na,nb,sa=0,sb=0;
int a[250],b[250];
int main(){
    fff();
    scanf("%d%d%d",&n,&na,&nb);
    for (int i=1;i<=na;i++) scanf("%d",&a[i]);
    for (int i=1;i<=nb;i++) scanf("%d",&b[i]);
    int i=1,j=1;
    while (n>0){
        n--;
        sa+=mp[a[i]][b[j]];
        sb+=mp[b[j]][a[i]];
        i++;
        j++;
        if(i==(na+1)) i=1;
        if(j==(nb+1)) j=1;
    }
    cout<<sa<<' '<<sb;
    return 0;
}

【题目描述】

  • 给你一棵树,树上有点权,相邻点之间距离为1。求任意两点之间距离为2的乘积之和与乘积的最大值。

  • 稍微有点难想。由于两点之间是相差为2的,则对于每一个点,在总的sum之间是增加所有孩子与父亲的乘积和所有孩子之间的乘积之和。最大值也可以找出第一大的孩子和第二大的孩子进行乘积维护。
  • 那么这一点在dfs时就可以进行操作了。
  • 而对于每两个孩子之间的乘积求和则可以利用数学上的多项和平方后展开求出。 ( a + b + c ) 2 = a 2 + b 2 + c 2 + 2 a b + 2 a c + 2 b c 则可以在dfs时求出 s 1 = w 1 + w 2 + . . . s 2 = w 1 2 + w 2 2 + . . .
  • 然后孩子之间的值就是 s 1 2 s 2
  • 自己的孩子对自己父亲的乘积就是 2 s 1 w f (由于有双向)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
void fff(){
    freopen("link.in","r",stdin);
    freopen("link.out","w",stdout);
}
const int MAXN=200100;
const int MOD=10007;
int n;
int w[MAXN];
struct Edge{
    int from,to;
};
vector <Edge> edge;
vector <int> G[MAXN];
int sum=0,maxx=0;
void dfs(int u,int fa){
    int siz=G[u].size();
    int sa=0,sb=0;
    int mxf=0,mxs=0;
    for (int i=0;i<siz;i++){
        Edge &e=edge[G[u][i]];
        if(e.to!=fa){
             if( w[e.to]>mxf){
                mxs=mxf;
                mxf=w[e.to];
             }else if(mxs<w[e.to]){
                mxs=w[e.to];
             }
             sa=(sa+w[e.to])%MOD;
             sb=(sb+w[e.to]*w[e.to]%MOD)%MOD;
             dfs(e.to,u);
        }
    }
    sum=(sum+sa*sa%MOD-sb+MOD+sa*w[fa]*2%MOD)%MOD;
    maxx=max(maxx,max(mxf*mxs,mxf*w[fa]));
}
int main(){
//  fff();
    scanf("%d",&n);
    for (int i=1;i<n;i++){
        int f,t;
        scanf("%d%d",&f,&t);
        edge.push_back((Edge){f,t});
        G[f].push_back(edge.size()-1);
        edge.push_back((Edge){t,f});
        G[t].push_back(edge.size()-1);
    }
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    dfs(1,0);
    cout<<maxx<<' '<<sum;
    return 0;
}

第三题——飞扬的小鸟(bird)

【题目描述】

(这道题太长了懒得概括..直接贴题目了)

  • 1.游戏界面是一个n*m的二维平面,其中有k个管道(忽略管道的宽度)。
  • 2.小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
  • 3.小鸟每个单位时间沿横坐标方向右移的距离为1,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度Y。小鸟位于横坐标方向不同位置时,上升的高度X和下降的高度Y可能互不相同。
  • 4.小鸟高度等于0或者小鸟碰到管道时,游戏失败。小鸟高度为m时,无法再上升。
  • 如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。

  • 第一次打了75分。(但好像暴力广搜就有85?加个优化有100?)
  • 一看就知道是一道dp题,关键就在于转移方程了。那么把这幅图建成m*n的地图。对于每一个位置有以下几种情况:
    • 1、当前点是前一个点往上跳k次的结果。
    • 2、当前点是前一个点往下掉的结果。
    • 而对于往上跳k次的情况由于会出现多次跳到同一个点浪费时间的情况,则可以将它转化为从当前点竖直下某个点向上跳一次(距离是前一步的x)的结果。而对于在天花板上的情况则只好枚举x的范围来进行取舍。
  • dp方程想好之后就是实现的问题。但博主也很奇怪..每次转移完之后要在不合法的地方重新进行置INF,不然后面几个点会爆。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define INF 0x3f3f3f3f
using namespace std;
void fff(){
    freopen("bird.in","r",stdin);
    freopen("bird.out","w",stdout);
}
const int MAXN=10010;
int n,m,k;
struct node{
    int x,y;
}a[MAXN];
bool vi[MAXN];
int l[MAXN],h[MAXN];
int dp[MAXN][1010];
int p;
int main(){
//  fff();
    memset(l,0,sizeof(l));
    memset(dp,INF,sizeof(dp));
    scanf("%d%d%d",&n,&m,&k);
    for (int i=0;i<n;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
        h[i+1]=m+1;
    }
    for (int i=1;i<=k;i++){
        scanf("%d",&p);
        scanf("%d%d",&l[p],&h[p]);
        vi[p]=true;
    }
    for (int i=1;i<=m;i++) dp[0][i]=0;
    for (int i=1;i<=n;i++){
        bool flag=false;
        for (int j=1;j<=m;j++){
            if(j>=a[i-1].x){
                dp[i][j]=min(dp[i][j],dp[i-1][j-a[i-1].x]+1);
                dp[i][j]=min(dp[i][j],dp[i][j-a[i-1].x]+1);
            }
            if(j==m){
                for (int k=j-a[i-1].x;k<=m;k++){
                    dp[i][j]=min(dp[i][j],min(dp[i][k]+1,dp[i-1][k]+1));
                }
            }
        }
        for (int j=l[i]+1;j<h[i];j++){
            if(j+a[i-1].y<=m) dp[i][j]=min(dp[i-1][j+a[i-1].y],dp[i][j]);
        }
        for (int j=1;j<=l[i];j++) dp[i][j]=INF;
        for (int j=h[i];j<=m;j++) dp[i][j]=INF;
    } 
    int cnt=k,ans=INF;
    for (int i=n;i>=1;i--){
        for (int j=l[i]+1;j<h[i];j++){
            if(dp[i][j]<INF) ans=min(ans,dp[i][j]);
        }
        if (ans!=INF) break;
        if(h[i]<=m) cnt--;
    }
    if(cnt==k) printf("1\n%d",ans);
    else printf("0\n%d",cnt);
    return 0;
}

猜你喜欢

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