牛客练习赛59题解报告

题目链接

A题.小乔和小灰灰

题意
给一个字符串
问字符串里有没有出现子序列"XiaoQiao"和“XiaoHuiHui”
题解
用两个字符串存入,并对给定字符串进行On查询

AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    string t="XiaoQiao";
    string s;cin>>s;
    int j=0;
    for(int i=0;i<s.length();i++){
        if(s[i]==t[j])j++;
        if(j==t.length())break;
    }
    if(j<t.length()){cout<<"emm"<<endl;return 0;}
    string tt="XiaoHuiHui";
    j=0;
    for(int i=0;i<s.length();i++){
        if(s[i]==tt[j])j++;
        if(j==tt.length())break;
    }
    if(j<tt.length())cout<<"emm"<<endl;
    else cout<<"Happy"<<endl;
    return 0;
}


B题.牛能和小镇

题意
在二维平面中给定n个点,每个点有坐标 xi,yi
需要让这n个点连通
第i个点和第j个点建路所需要的花费为
| x x x2i y y yi x x x1j y y yj + y y y2i( y y yi 2 2 2 x x xi ) — y y y2j( y y yj 2 2 2 x x xj ) |
问最少需要花费多少
题解
将公式的 i i i j j j分离开化简,这里只看 i i i

x x x2i y y yi + y y y2i ( y y yi 2 2 2 x x xi )
由此式子化简可得到
y y yi( x x xi y y yi )2

所以将每个点的值计算出来,排序后让相邻两条建路是最短的
由于 | a a a1 a a a2 + a a a2 a a a3 ············· a a an-1 a a an |
可以推出最后的结果应该是 a a an a a a1

AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f;
vector<ll> a;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;cin>>n;
    for(int i=1;i<=n;i++){
        ll x,y;cin>>x>>y;
        a.pb(y*(x-y)*(x-y));
    }
    sort(all(a));
    cout<<*(a.end()-1)-*a.begin()<<endl;
    return 0;
}


C题.装备合成

题意
x x x件材料 a a a        y y y件材料 b b b   有两种方法合成装备
2 2 2件材料 a a a 3 3 3件材料 b b b可以合成一套装备
4 4 4件材料 a a a 1 1 1件材料 b b b可以合成一套装备
问最多能合成多少套装备

题解
官方题解用的是三分
我用的方法是贪心,尽量先用 2 2 2件材料 a a a 3 3 3件材料 b b b可以合成一套装备,当材料 a a a和材料 b b b剩余个数是4:1的的时候开始用第二种方法合成装备
设使两个材料之比变成4:1需要用第一种方法合成 n n n
x − 2 n y − 3 n = 4 \frac{x-2n}{y-3n}\quad = 4 y3nx2n=4
化简可以得到
4 y − x = 10 n 4y-x=10n 4yx=10n
所以可以得到
a n s = { m i n ( x / 4 , y ) , 4 y - x < 0 d + m i n ( x / 4 , y ) , others ans = \begin{cases} min(x/4,y), & \text{4$y$-$x$<$0$} \\ d+min(x/4,y), & \text{others} \end{cases} ans={min(x/4,y),d+min(x/4,y),4y-x<0others
(其中 d d d表示化成4:1需要多少次)
d = 4 y − x 10 d=\frac{4y-x}{10}\quad d=104yx
当然可能这个值不会被整除,所以要通过四舍五入判断是应该多算一次第一种情况还是多算一次第二种情况,所以式子变为
d = 4 y − x + 5 10 d=\frac{4y-x+5}{10}\quad d=104yx+5

AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;cin>>t;
    while(t--){
        ll x,y;
        cin>>x>>y;
        if(4*y<x){cout<<y<<endl;continue;}
        ll d=(4*y-x+5)/10;
        d=min(x/2,d),d=min(y/3,d);
        ll ans=0;
        ans+=d;
        x-=2*d,y-=3*d;
        cout<<ans+min(x/4,y)<<endl;
    }
    return 0;
}


D题. 取石子游戏

题意
A 和 B A和B AB轮流取石子,一共有 n n n个石子, A A A先开始取
假设当前的石子有 k k k
k = 1 k=1 k=1的时候B获得胜利
k > = 2 k>=2 k>=2的时候将式子分为 f ( k ) f(k) f(k) k − f ( k ) k-f(k) kf(k)两堆,必须取走其中一堆, f ( k ) = x f(k)=x f(k)=x为满足 x ∗ 2 < = k x*2<=k x2<=k的最大整数
最后问谁能获得胜利
题解
由于 n n n的范围是 1 e 18 1e18 1e18,所以不能采取暴力的方法计算
需要打表找规律
如果 A A A上次连续赢的长度为 y y y,可以发现 B B B会连续赢 2 ∗ y − 1 2*y-1 2y1
如果 B B B上次连续赢的长度为 y y y,可以发现 A A A会连续赢 2 ∗ y + 1 2*y+1 2y+1
所以可以使用 O ( l o g   2   n ) O(log~2~n) O(log 2 n)的复杂度去找n在谁获胜的区间里

AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;cin>>t;
    while(t--){
        ll n;
        cin>>n;
        ll x=1,y=2;
        bool f=1;
        while(x<n){
            if(!f)x+=y,y=2*y+1,f=1;
            else x+=y,y=y*2-1,f=0;
        }
        if(!f)cout<<"XiaoHuiHui"<<endl;
        else cout<<"XiaoQiao"<<endl;
    }
    return 0;
}


E题.石子搬运

题意
一共有 n n n堆石子,每堆有 a i   ai~ ai 个石子,牛牛要把它们搬走
如果一次搬了 k k k个石子,会对牛牛产生 k 2 k^2 k2的负担
牛牛最多搬 m m m
牛能每次会将第 x x x堆的石子数量变为 v v v,所以每次需要重新计算牛牛的最小负担(总共 q q q次)
题解
由于数据较小
n , k , m , q < = 400 n,k,m,q<=400 n,k,m,q<=400
可以采用 d p dp dp的方法,但是由于石子的数量是改变的,可以通过线段树来优化 d p dp dp
d p [ i ] [ j ] dp[i][j] dp[i][j]是在线段树的第 i i i个结点,把第 i i i组分隔 j j j次的最小负担 ( 分 隔 j 次 , 总 共 分 出 j + 1 组 ) (分隔j次,总共分出j+1组) (jj+1)

状态转移方程如下:
先 将 叶 子 结 点 的 0 到 m − n 次 分 割 的 最 小 情 况 计 算 出 来 先将叶子结点的0到m-n次分割的最小情况计算出来 0mn

然 后 通 过 转 移 方 程 推 广 到 各 个 非 终 端 结 点 然后通过转移方程推广到各个非终端结点 广

d p [ p ] [ i + j ] = m i n ( d p [ i ] [ j ] , d p [ l s o n ] [ i ] + d p [ r s o n ] [ j ] dp[p][i+j]=min(dp[i][j],dp[lson][i]+dp[rson][j] dp[p][i+j]=min(dp[i][j],dp[lson][i]+dp[rson][j]

最 后 a n s = d p [ 1 ] [ m − n ] 最后ans=dp[1][m-n] ans=dp[1][mn]

AC代码

#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=2e5+10;
const ll inf=0x3f3f3f3f;

ll dp[410<<2][410];
int n,m;

void update(int p,int l,int r,int x,ll v){
    if(l==r){
        for(int i=0;i<=m-n;i++){
            ll t=v/(i+1);
            ll a=v%(i+1),b=(i+1)-a;
            dp[p][i]=b*t*t+a*(t+1)*(t+1);
        }
        return;
    }
    int mid=l+r>>1;
    if(x<=mid)update(p<<1,l,mid,x,v);
    else update(p<<1|1,mid+1,r,x,v);
    for(int i=0;i<=m-n;i++)
        dp[p][i]=inf*inf;
    for(int i=0;i<=m-n;i++)
        for(int j=0;i+j<=m-n;j++)
            dp[p][i+j]=min(dp[p][i+j],dp[p<<1][i]+dp[p<<1|1][j]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n>>m;
    for(int i=1,x;i<=n;i++){
        cin>>x;
        update(1,1,n,i,x);
    }
    int q;cin>>q;
    while(q--){
        int x,v;
        cin>>x>>v;
        update(1,1,n,x,v);
        cout<<dp[1][m-n]<<endl;
    }
    return 0;
}

你可能感兴趣的:(牛客练习赛59题解报告)