AtCoder Beginner Contest 169(B,C,D唯一分解,E 数学分析 F(dp))

题目链接

B - Multiplication 2

做法:题目很简单,但是各种姿势不给A就很恶心,__int128 wa,ull 也wa,最后用java大数才A,服了。

看了别人的代码发现,用了一种 判断爆long long 的方法 ,具体看代码吧。

c++代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll solve() {
    ll N, a;
    cin >> N;
    vector<ll> A(N);
    for ( int i = 0; i < N; i++ ) {
        cin >> A[i];
        if ( A[i] == 0 ) return 0;//?????没输入完就返回了?
    }
    ll mx = 1e18;
    ll m = 1, p = 1;
    for ( int i = 0; i < N; i++ ) {
        m *= A[i];
        if ( m < p || m > mx ) return -1;//比之前的小了,就是爆longlong了
        p = m;
    }
    return m;
}

int main() {
    auto ans = solve();
    cout << ans << "\n";
    return 0;
}

Java代码:

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        BigInteger N=new BigInteger("1000000000000000000");
        BigInteger ans=new BigInteger("1");
        BigInteger n0=new BigInteger("0");


        int flag=1;
        int num=1;
        for(int i=1;i<=n;++i){
            BigInteger x=sc.nextBigInteger();
            if(x.compareTo(n0)==0) num=0;
            if(flag==0) continue;
            ans=ans.multiply(x);
            if(ans.compareTo(N)>0) {
                flag=0;
            }
        }

        if(num==0) System.out.println(0);
        else if(ans.compareTo(N)>0||flag==0) System.out.println("-1");
        else System.out.println(ans);
    }
}

C - Multiplication 3

被这两题给恶心到了,-0.5 wa  round 也wa,最后用java 的ROUND_DOWN过的。。

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);


        BigDecimal a,b;
        a=sc.nextBigDecimal();
        b=sc.nextBigDecimal();
        BigDecimal ans=a.multiply(b);



        System.out.println(ans.setScale(0, BigDecimal.ROUND_DOWN));
    }
}

D - Div Game

唯一分解一下就好了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}


int main()
{
    ll n=read();
    int ans=0;
    for(ll i=2;i*i<=n;++i){
        if(n%i==0){
            int num=0;
            while(n%i==0) num++,n=n/i;

            int l=1,r=1000,t=0;
            //printf("i:%lld num:%d\n",i,num);

            while(l<=r){
                int mid=l+r>>1;
                int res=mid*(mid+1)/2;
                if(res>num) r=mid-1;
                else t=mid,l=mid+1;
            }
            //printf("t:%d\n",t);
            ans+=t;

        }
    }
    if(n!=1) ans++;

    printf("%d\n",ans);
}

E - Count Median

题意:给你n个区间[li,ri] n个数a[i]分别从n个区间内选一个,问形成的序列不同中位数的个数是多少

题解思路来自b站大佬:链接

做法:感觉这类题型见过不下四遍了,而且不同题还有不同的解法,要么是列公式 连续区间求导还是求和(牛客某次练习赛最后一题),要么就是跟这题一样,数学分析分析

(ccpc wannfly 训练营重现赛某题:期望逆序数

牛客挑战赛36 序列

还有一个忘记在哪个地方了。

这题的思路大概就是

对于一个 x  能否成为中位数,判断他的充要条件是什么。

视频里说了结论:

对于n是奇数来说,

1、右端点 小于x的 区间 个数不超过n/2+1个,

2、左端点 大于x的 区间 个数不超过n-(n/2)=n/2+1

对于n是偶数

1、用上面的方法会找到两个合法的区间

[a1,b1]  [a2,b2] 由于n数的中位数是 中间两个数之和/2  所以这里的合法区间就是 b1+b2-(a1+a2)+1

那么如何求这些个x呢?对左端点集a排个序,中间的数就是满足充要条件的,右端点集b 也类似

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
long long a[200010], b[200010];
int n;
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i] >> b[i];
    }
    sort(a + 1, a + n + 1);
    sort(b + 1, b + n + 1);
    if (n % 2) {
        //printf("b:%lld a:%lld\n",b[n/2+1],a[n/2+1]);
        cout << b[n / 2 + 1] - a[n / 2 + 1] + 1;
    } else {
        cout << b[n / 2] + b[n / 2 + 1] - a[n / 2] - a[n / 2 + 1] + 1;
    }
    return 0;
}
/*
7
1 3
2 4
5 8
6 9
7 10
12 14
13 15
*/

F - Knapsack for All Subsets

做法:不考虑子集的话就是很水的dp 设dp[i][j]为前i项子集和为j的方案数。

转移方程:

for(int j=s;j>=a[i];--j){
     add(dp[i][j],dp[i-1][j-a[i]]);
}

然而这题是求所有子集的 再求上面的问题。

其实也很简单。 设dp[i][j]为前i项子集和为j的方案数。 每次新增i  dp[i][j]=dp[i-1][j]*2.

为什么乘2呢,因为新增的a[i] 选 可以与前面的所有子集构成一次子集,若a[i]不选,就是继承前面的,那就是乘2了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const ll mod=998244353;
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=3e3+10;
ll dp[N][N];
int a[N],n,s;
void add(ll &x,ll y)
{
    x=(x+y)%mod;
}
int main()
{
    n=read(),s=read();
    rep(i,1,n) a[i]=read();
    dp[0][0]=1;
    rep(i,1,n)
    {
        for(int j=0;j<=s;++j) dp[i][j]=dp[i-1][j]*2%mod;

        for(int j=s;j>=a[i];--j){
            add(dp[i][j],dp[i-1][j-a[i]]);
        }
    }
    printf("%lld\n",dp[n][s]);
}

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/106482391
今日推荐