AtCoder Beginner Contest 155 - D - Pairs ,-E - Payment 題解


AtCoder Beginner Contest 155 - D - Pairs ,-E - Payment 題解

D - Pairs

We have NN integers A1,A2,...,ANA1,A2,...,AN.

There are N(N−1)2N(N−1)2 ways to choose two of them and form a pair. If we compute the product of each of those pairs and sort the results in ascending order, what will be the KK-th number in that list?

題意:

給定一個含有n個整數的數組a,將其所有點對\((i,j),i \neq j\),兩兩相乘\(a[i]*a[j]\) 得到\(\frac{N(N-1)}{2}\)個數,問將其升序排序后第k個數是多少?

思路:

\(a[i]\) 分為3類:大於0,等於0,小於0。分別加入vector容器 v1,v0,v2中后排序容器。

然后再\([-1e18,1e18]\) 中二分答案ans,

然后以時間復雜度為\(O(n*log_2(n))\) 的方法去計算有多少對數相乘小於ans,方法為:

如果ans小於0:

​ 只需要遍歷v1中的每一個元素y,然后二分找到v2中與y相乘小於ans的下標,然后更新計數。

如果ans等於0:

​ 小於ans的一定是負數,那么只有正數與負數相乘才會得到負數,所以計數=\(v1.size()*v2.size()\)

如果ans大於0:

負數和零一定小於ans,所以計數先加上\(v1.size()*v2.size()+v0.size()*(v1.size()+v2.size())\)

然后再找正數中小於ans的乘積對:

​ 遍歷v1中的每一個元素y,然后二分找到v1中與y相乘小於ans的下標,更新計數。

​ 遍歷v2中的每一個元素y,然后二分找到v2中與y相乘小於ans的下標,更新計數。

對於vector容器的二分我們可以使用lower_bound()和upper_bound() 兩個函數。

二分過程中可能會用到\(ceil(\frac{x}{y})= \left \lceil \frac{x}{y} \right \rceil=\frac{x+y-1}{y}\),可以避免精度丟失引起的誤差。

具體可以看代碼:

代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline long long readll() {long long tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
inline int readint() {int tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
ll k, n;
ll a[maxn];
std::vector<ll> v1, v2;
ll len1, len2;
std::vector<ll> v;
ll cnt = 0ll;
bool check(ll x)
{
    ll res = 0ll;
    if (x > 0)
    {
        for (auto y : v1)
        {
            res += lower_bound(ALL(v), (x + y - 1) / y) - v.begin();
            v.push_back(y);
        }
        v.clear();
        for (auto y : v2)
        {
            res += lower_bound(ALL(v), (x + y - 1) / y) - v.begin();
            v.push_back(y);
        }
        v.clear();
        res += cnt * (cnt - 1) / 2;
        res += 1ll * sz(v1) * sz(v2);
        res += cnt * (sz(v1) + sz(v2));
    } else if (x < 0)
    {
        x = -x;
        for (auto y : v1)
        {
            res += sz(v2) - (upper_bound(ALL(v2), x / y) - v2.begin());
        }
    } else
    {
        res += 1ll * sz(v1) * sz(v2);
    }
    return res < k;
}
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    n = readll();
    k = readll();
    repd(i, 1, n)
    {
        a[i] = readll();
        if (a[i] > 0)
        {
            v1.push_back(a[i]);
        } else if (a[i] < 0)
        {
            v2.push_back(-a[i]);
        } else
        {
            cnt++;
        }
    }
    sort(ALL(v1));
    sort(ALL(v2));
    len1 = sz(v1);
    len2 = sz(v2);
    ll l = -1e18;
    ll r = 1e18;
    ll mid;
    ll ans;
    while (l <= r)
    {
        mid = (l + r) >> 1;
        if (check(mid))
        {
            l = mid + 1;
            ans = mid;
        } else
        {
            r = mid - 1;
        }
    }
    printf("%lld\n", ans );
    return 0;
}

E - Payment

In the Kingdom of AtCoder, only banknotes are used as currency. There are 10100+110100+1 kinds of banknotes, with the values of 1,10,102,103,…,10(10100)1,10,102,103,…,10(10100). You have come shopping at a mall and are now buying a takoyaki machine with a value of NN. (Takoyaki is the name of a Japanese snack.)

To make the payment, you will choose some amount of money which is at least NN and give it to the clerk. Then, the clerk gives you back the change, which is the amount of money you give minus NN.

What will be the minimum possible number of total banknotes used by you and the clerk, when both choose the combination of banknotes to minimize this count?

Assume that you have sufficient numbers of banknotes, and so does the clerk.

題意:

在一個國家紙幣只有\(10^{100}+1\)種面額,分別是\(1, 10, 10^2, 10^3, \dots, 10^{(10^{100})}\)

你需要去支付n元,可能是你付出了一個更大的錢數,對方再找回你差值的錢。

問整個交易過程最少需要用到多少個紙幣?

思路:

我們從低位到高位看,第i位x,是選擇方法1:付x個\(10^i\)元的紙幣

還是選擇方法2:付一個\(10^{i+1}\)元的紙幣在讓對方找\(10-x\)\(10^i\) 紙幣呢?

我們可以看出對於每一位i,只有2個選擇情況,即上述2種。

那么我們不妨定義動態規划的狀態\(dp[i][0]\)代表從低數第i位選擇了方法1,\(dp[i][1]\)代表從低數第i位選擇了方法2.

定義初始狀態:

dp[n][0] = (s[n] - '0');
dp[n][1] = 10 - (s[n] - '0');

轉移方程:

dp[i][0] = min(dp[i + 1][0] + (s[i] - '0'), dp[i + 1][1] + (s[i] - '0') + 1);
dp[i][1] = min(dp[i + 1][0] + 10 - (s[i] - '0'), dp[i + 1][1] + 10 - (s[i] - '0' + 1)); 

代碼:

char s[maxn];
ll dp[maxn][2];
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    s[0] = '0';
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    dp[n][0] = (s[n] - '0');
    dp[n][1] = 10 - (s[n] - '0');
    for (int i = n - 1; i >= 0; --i)
    {
        dp[i][0] = min(dp[i + 1][0] + (s[i] - '0'), dp[i + 1][1] + (s[i] - '0') + 1);
        dp[i][1] = min(dp[i + 1][0] + 10 - (s[i] - '0'), dp[i + 1][1] + 10 - (s[i] - '0' + 1));
    }
    printf("%lld\n", min(dp[0][0], dp[0][1]));
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM