牛客算法周周练8

闲扯

A题当时在想排列组合,然后看了下过题数,这要是排列组合也太不科学了...
E题是2019宁夏区域赛现场赛的原题,好像是B题,数据都没变,原代码直接ac
D不会,看不懂,看懂了想扇死自己,一个纯暴力map............
C的写2的那种情况以为是直接写一个线段树,然后直接查改,然后没想到这东东是差分...然后3的那种情况没想到是差分做的,当时没理解2的那种情况的引导
B没啥说的,第一眼想这是dp,我去,这咋dp,哦,数据范围30,那没了,直接爆搜

A---小A买彩票

题意:小A买n张彩票,然后每张中的金额为1,2,3,4之一,然后每种等可能出现,也就是图片说明 ,每张彩票3元,问不亏本的概率是多少,然后输出其分数形式
题解:图片说明 这个就是答案,因为n比较小,所以可以直接pow()算次方
比如n=2的时候,存在图片说明 6种情况,然后每种出现的可能都是图片说明,所以相加就是图片说明
然后我们可以写一个dp数组,来求解组合出同一种金额有多少种可能图片说明
然后把大于3*n的金额数的可能求和

#include<bits/stdc++.h>
#define IOS std::ios::sync_with_stdio(false);
#define pb push_back
#define ll long long
#define mod 1e9+7
using namespace std;
ll dp[31][121]={0};//表示赚的数目

ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}

int main()
{
    IOS
    dp[0][0]=1;
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=4*n;j++)
        {
            if(j-1>=0)
                dp[i][j]+=dp[i-1][j-1];
            if(j-2>=0)
                dp[i][j]+=dp[i-1][j-2];
            if(j-3>=0)
                dp[i][j]+=dp[i-1][j-3];
            if(j-4>=0)
                dp[i][j]+=dp[i-1][j-4];
        }
    }
    ll a=0;
    for(int i=n*3;i<=121;i++)
        a+=dp[n][i];
    ll b=pow(4,n);
    ll c=a/gcd(a,b);
    ll d=b/gcd(a,b);
    printf("%lld/%lld\n",c,d);
    return 0;
}

B---「金」点石成金

题意:给n个元素,每个元素有4个属性
(1)增加ai的财富,消耗bi的魔法
(2)回复ci的魔法,减少di的财富
然后:收益值=财富值*魔法值
另外:数值不会变为负数,即任何时候,如果数值小于了0,它会立即变为0
求最大收益值
题解:数据比较小,直接爆搜
每次判断是增加财富,还是增加魔法,再注意数值大小就行

#include<bits/stdc++.h>
#define MAX_INT  ((unsigned)(-1)>>1)
#define MIN_INT  (~MAX_INT)
#define pi 3.1415926535898
typedef long long ll;
#define inf 0x3f3f3f3f
#define infmax 0x3f3f3f3f3f3f3f3f
using namespace std;
ll read()
{
    ll c=0;int flag=1;
    char s;
    while((s=getchar())>'9'||s<'0')if(s=='-')flag=-1;
    c=s-'0';
    while((s=getchar())<='9'&&s>='0') c=c*10+s-'0';
    return c*flag;
}
ll a[20],b[20],c[20],d[20];
ll ans=0;
int n;
void dfs(int i,ll p,ll q)//第i块石头,p魔法,q财富
{
    if(i==n+1){
         ans=max(ans,p*q);
         return;
    }

    //if(p>0){
    if(p-b[i]<0)//选择增加a财富
        dfs(i+1,0,q+a[i]);
    else dfs(i+1,p-b[i],q+a[i]);
    //}

    if(q-d[i]<0)//增加c魔法
        dfs(i+1,p+c[i],0);
    else dfs(i+1,p+c[i],q-d[i]);
}
int main(void)
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i]>>b[i]>>c[i]>>d[i];
    dfs(1,0,0);
    cout<<ans;
    return 0;
}

C---小阳的贝壳

题意:有n个数,然后m个操作
1 l r x:给 [l,r]区间里所有贝壳的颜色值加上 x 。

2 l r:询问[l,r]区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l = r输出 0)。

3 l r :询问 [l,r][l,r] 区间里所有贝壳颜色值的最大公约数。
题解:
数学的能力有限解释不清
转一下别人的
图片说明

#include<bits/stdc++.h>
#define ls rt << 1
#define rs ls | 1
using namespace std;
const int MAXN = 1e5 + 5;

int sum[MAXN * 4], max_[MAXN * 4];  // sum是求和数组, max_是最大值
int gc[MAXN * 4], p[MAXN];   // gc是最大公约数, p是差分数组
int n, m;
int gcd(int a, int b) {
    return !b ? a : gcd(b, a % b);
}
void push_up(int rt) {  操作
    sum[rt] = sum[ls] + sum[rs];
    max_[rt] = max(max_[ls], max_[rs]);
    gc[rt] = gcd(gc[ls], gc[rs]);
}
void build(int rt, int l, int r) {  // 建树
    if (l == r) {
        sum[rt] = p[l];
        max_[rt] = gc[rt] = abs(p[l]);  // 注意要加绝对值
        return;
    }
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    push_up(rt);
}
void update(int rt, int X, int l, int r, int w) {  // 更新操作
    if (l == r) {
        p[l] += w;  
        sum[rt] = p[l];
        max_[rt] = gc[rt] = abs(p[l]);  // 加绝对值
        return;
    }
    int mid = (l + r) >> 1;
    if (mid >= X) update(ls, X, l, mid, w);
    else update(rs, X, mid + 1, r, w);
    push_up(rt);
}
int sum_query(int rt, int L, int R, int l, int r) {  // 区间求和
    if (l >= L && r <= R) return sum[rt];
    int mid = (l + r) >> 1, res = 0;
    if (mid >= L) res += sum_query(ls, L, R, l, mid);
    if (mid < R) res += sum_query(rs, L, R, mid + 1, r);
    return res;
}
int max_query(int rt, int L, int R, int l, int r) {  // 区间最大值
    if (l >= L && r <= R) return max_[rt];
    int mid = (l + r) >> 1, res = 0;
    if (mid >= L) res = max(res, max_query(ls, L, R, l, mid));
    if (mid < R) res = max(res, max_query(rs, L, R, mid + 1, r));
    return res;
}
int gcd_query(int rt, int L, int R, int l, int r) {  // 区间最大公约数
    if (l >= L && r <= R) return gc[rt];
    int mid = (l + r) >> 1, res = 0;
    if (mid >= L) res = gcd(res, gcd_query(ls, L, R, l, mid));
    if (mid < R) res = gcd(res, gcd_query(rs, L, R, mid + 1, r));
    return res;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) scanf("%d", &p[i]);
    for (int i = n; i >= 1; i--) p[i] -= p[i - 1];   // 差分数组
    build(1, 1, n); 
    while (m--) {
        int L, R, W, op;
        scanf("%d%d%d", &op, &L, &R);
        if (op == 1) {
            scanf("%d", &W);  
            update(1, L, 1, n, W);  更新L
            if (R < n) update(1, R + 1, 1, n, -W);  // 更新R + 1
        }
        else if (op == 2) {
            if (L == R) { cout << 0 << endl; continue; }
            cout << max_query(1, L + 1, R, 1, n) << endl;
        }
        else
            cout << gcd(sum_query(1, 1, L, 1, n), gcd_query(1, L + 1, R, 1, n)) << endl;
    }
}

D---Forsaken喜欢字符串

题意:佩服出题人能把题目写的这么复杂.......那个公式看懵了
输入n个长度为k的字符串,然后m次查询,输入x,len,每次查询第x个串种长度为len的子串,在其他串里出现的次数,最后再乘以len,再求出的答案不能包括第x个串的子串
题解:map,map,map
直接先把所有串的子串都求一下,然后统计每种子串的出现的数量
然后对于每次查询的时候,求一下当前串种长度为len的所有的子串,然后再用第x个串中的每种子串,在所有串中的总数减去在第x个串里出现的数量最后乘以len

#include<bits/stdc++.h>
#define ll long long
const int maxn=5e4+5;
using namespace std;
map<string,int>a,aa;
int main(){
    int n,k,q;
    string s[maxn];
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>s[i];
        for(int j=1;j<=k;j++)
            for(int t=0;t<=k-j;t++) a[s[i].substr(t,j)]++;
    }
    cin>>q;
    for(int i=1;i<=q;i++){
        int x,l;
        ll ans=0;
        cin>>x>>l;
        aa.clear();
        for(int j=0;j<=k-l;j++)
         aa[s[x].substr(j,l)]++;
        for(int j=0;j<=k-l;j++)
        ans+=a[s[x].substr(j,l)]-aa[s[x].substr(j,l)];
        cout<<ans*l<<endl;
    }
}
//code:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=43833751

E---Board

题意:数组中每一个元素都是0,可以将某一行的所有元素同时加上一个值,也可以将某一列的所有元素同时加上一个值,在几次操作后,一个元素被隐藏了,隐藏的数是几?-1表示隐藏的元素
题解:她正着生成一个矩阵,那我们可以反着把这个矩阵还原为,除-1元素所在位置以外的矩阵其他元素为0,然后如果每次还原位置经过-1元素的位置,-1元素同样也减减,最后输出-1元素位置的结果*(-1)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+5;
int a[N][N];
int main()
{
    int n;
    int ti, tj;
    cin>>n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin>>a[i][j];
            if (a[i][j] == -1)
            {
                ti = i;
                tj = j;
                a[i][j] = 0;
            }
        }
    }
    int minn;
    for (int i = 1; i <= n; i++)
    {
        minn = 9999999;
        for (int j = 1; j <= n; j++)
        {
            if (i != ti || j != tj)
            {
                minn = min(minn, a[i][j]);
            }
        }
        for (int j = 1; j <= n; j++)
        {
            a[i][j] -= minn;
        }
    }
    for (int j = 1; j <= n; j++)
    {
        minn = 9999999;
        for (int i = 1; i <= n; i++)
        {
            if (i != ti || j != tj)
                minn = min(minn, a[i][j]);
        }
        for (int i = 1; i <= n; i++)
        {
            a[i][j] -= minn;
        }
    }
    cout<<-a[ti][tj]<<endl;
    return 0;
}
全部评论

相关推荐

点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务