Educational Codeforces Round 80 (Rated for Div. 2)

A. Deadline

如果n>=d 直接yes

否则对于公式

\(x+ceil(d/(x+1))<=n\)

两边同时乘以x+1

会得到一个关于x的一元二次方程,

通过求根公式解出较小的那一个根x1,

然后在\(x1+-3\) 的范围内找到\(x+ceil(d/(x+1))\) 的最小值与n比较即可。

#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 gg(x) getInt(&x)
#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 void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int t;
ll n, d;
ll check(ll x)
{
    return x + ceil(1.0 * d / (x + 1));
}
typedef long double ld;
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    // gbtb;
    cin >> t;
    while (t--)
    {

        cin >> n >> d;
        if (d <= n)
        {
            cout << "YES" << endl;
        } else
        {
            int isok = 0;
            ld b = 1 - n;
            ld c = d - n;
            ld xw = (-b - sqrtl(b * b - 4.0 * c)) * 0.5;
            ll xx = ll(xw);
            ll w = inf;
            repd(i, max(0ll, xx - 3), min(n * 1ll, xx + 3))
            {
                w = min(w, check(i));
            }
            if (w <= n)
            {
                isok = 1;
            }
            if (isok)
            {
                cout << "YES" << endl;
            } else
            {
                cout << "NO" << endl;
            }
        }
    }

    return 0;
}

inline void getInt(int* p) {
    char ch;
    do {
        ch = getchar();
    } while (ch == ' ' || ch == '\n');
    if (ch == '-') {
        *p = -(getchar() - '0');
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 - ch + '0';
        }
    }
    else {
        *p = ch - '0';
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 + ch - '0';
        }
    }
}

B. Yet Another Meme Problem

通过打表可以发现

满足 \(a \cdot b + a + b = conc(a, b)\) 的b 为9 ,99,999,9999...等等

所以答案即为 A*F(B)

F(B) 为 <=B 的 9 ,99,999,9999...等等的个数

F(B) 的求法非常简单。

int t;
ll a, b;
ll F(ll x)
{
    ll res = 0ll;
    ll base = 9ll;
    while (base <= x)
    {
        base *= 10;
        base += 9;
        res++;
    }
    return res;
}
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    gbtb;
    cin >> t;
    while (t--)
    {
        cin >> a >> b;
        a *= F(b);
        cout << a << endl;
    }
    return 0;
}

C. Two Arrays

\(dp1[i][j]\) 为长度为i,以j为结尾的单调不下降序列方案数

\(dp2[i][j]\) 为长度为i,以j为结尾的单调不上升序列方案数

当j1>=j2 时 以j1为结尾的单调不下降序列和以j2为结尾的单调不上升序列

自动满足:

  • ai≤bi for any index ii from 11 to mm;
  • array a is sorted in non-descending order;
  • array b is sorted in non-ascending order.

然后扫一遍计算答案即可,可以用前缀和来优化,但是这个数据范围没啥必要。

ll dp1[11][1111];
ll dp2[11][1111];
 
ll n;
ll m;
const ll mod = 1e9 + 7;
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    cin >> n >> m;
    repd(i, 1, n)
    {
        dp1[1][i] = 1;
        dp2[1][i] = 1;
    }
    repd(i, 2, m)
    {
        repd(j, 1, n)
        {
            repd(k, 1, j)
            {
                dp1[i][j] += dp1[i - 1][k];
                dp1[i][j] %= mod;
            }
            repd(k, j, n)
            {
                dp2[i][j] += dp2[i - 1][k];
                dp2[i][j] %= mod;
            }
        }
    }

    ll ans = 0ll;
    repd(i, 1, n)
    {
        repd(j, 1, i)
        {
            ans += dp2[m][i] * dp1[m][j] % mod;
            ans %= mod;
        }
    }
    cout << ans << endl;
    return 0;
}

D. Minimax Problem

极小极大问题 普遍容易通过转化为二分极小值,然后验证可行性的问题。

本题也不例外,我们二分\(\min \limits_{k = 1}^{m} b_k\) 的数值mid。

然后去check 是否存在两个数组满足此条件。

check的方法如下:

我们遍历这n个数组,对于第i个数组的第j个数a(i,j) 是否大于等于mid用一个int整数进行二进制的状态压缩表示,然后将int数值加入一个unordered_set 中。

然后对unset中的元素进行两两测试是否按位与起来满足 m个数均为1.

为什么要用unset呢?

紧扣题目的数据范围,m<=8, 那么对于每一次check一共也就只有 2^8 个情况,

两两测试的时候最大也就是2^16次,依次来降低时间复杂度。

二分的时间复杂度时O(1e9)

check的时间复杂度为O(n*m+2^16)

总时间复杂度为:\(O(log_2(1e9)*(n*m+2^{16}))\)

#include <bits/stdc++.h>
#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 gg(x) getInt(&x)
#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 void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n, m;
int a[300010][9];
unordered_set<int> st;
int t1, t2;
bool check(int x)
{
    st.clear();
    int t;
    repd(i, 1, n)
    {
        t = 0;
        repd(j, 1, m)
        {
            if (a[i][j] >= x)
            {
                t |= (1 << (j - 1));
            }
        }
        st.insert(t);
    }
    for (auto y : st)
    {
        for (auto z : st)
        {
            if ((y | z) == (1 << m) - 1)
            {
                t1 = y;
                t2 = z;
                return 1;
            }
        }
    }
}
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    du2(n, m);
    repd(i, 1, n)
    {
        repd(j, 1, m)
        {
            du1(a[i][j]);
        }
    }
    int l = 0;
    int r = 1e9;
    int mid;
    int ans;
    while (l <= r)
    {
        mid = (l + r) >> 1;
        if (check(mid))
        {
            l = mid + 1;
            ans = mid;
        } else
        {
            r = mid - 1;
        }
    }
    int ans1, ans2;
    repd(i, 1, n)
    {
        int t = 0;
        repd(j, 1, m)
        {
            if (a[i][j] >= ans)
            {
                t |= (1 << (j - 1));
            }
        }
        if (t == t1)
        {
            ans1 = i;

        }
        if (t == t2)
        {
            ans2 = i;
        }
    }
    printf("%d %d\n", ans1, ans2 );

    return 0;
}

inline void getInt(int* p) {
    char ch;
    do {
        ch = getchar();
    } while (ch == ' ' || ch == '\n');
    if (ch == '-') {
        *p = -(getchar() - '0');
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 - ch + '0';
        }
    }
    else {
        *p = ch - '0';
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 + ch - '0';
        }
    }
}


 

E. Messenger Simulator

对于n个数的数组,我们先初始为 n,n-1,n-2,,,,3 ,2,1 的情况 (即n~1的降序排列)

然后把给定的m个数依次放在数组的尾部。

例如样例1 为:

5 4 3 2 1 3 5 1 4

定义数组vis[i] 代表数字i上一次出现在哪个位置,初始为0。

我们维护两个数组 be[i] 代表数字i出现的最前的位置,ed[i] 代表数字i 出现 的最后的位置。

如果有把数字i提前到第一位,be[i]=1,否则be[i]=i

ed[i] 为上述数组中紧挨着的两个i之间的数字种类数,或者最后一个数字i到结束有多少种类的数字。

因为如果出现第2次以及以上次数i时,证明将数字i提到数组的最前端了,那么i之前的位置一定是一个偏后面的位置,我们维护ed[i]的最大值即可。

至于“ed[i]的另一个情况,最后一个数字i到结束有多少种类的数字”,可以通过样例自行理解一下。

因为询问的区间均为有序的区间,所以可以直接用树状数组进行维护即可。

#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 gg(x) getInt(&x)
#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 void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n, m;
int a[maxn];
int tree[maxn];
int lowbit(int x)
{
    return x & (-x);
}
void add(int pos, int val)
{
    while (pos < maxn)
    {
        tree[pos] += val;
        pos += lowbit(pos);
    }
}
int ask(int pos)
{
    int res = 0;
    while (pos>0)
    {
        res += tree[pos];
        pos -= lowbit(pos);
    }
    return res;
}

int be[maxn];
int ed[maxn];
int vis[maxn];
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    du2(n, m);
    repd(i, 1, n)
    {
        a[i] = n - i + 1;
    }
    repd(i, n + 1, n + m)
    {
        du1(a[i]);
        be[a[i]] = 1;
    }
    repd(i, 1, n)
    {
        if (!be[i])
        {
            be[i] = i;
        }
    }
    repd(i, 1, n + m)
    {
        if (!vis[a[i]])
        {
            add(i, 1);
        } else
        {
            add(vis[a[i]], -1);
            add(i, 1);
        }
//        chu(i);
        if (vis[a[i]])ed[a[i]] = max(ed[a[i]], ask(i) - ask(vis[a[i]] - 1));
        vis[a[i]] = i;
    }
    repd(i, 1, n)
    {
        ed[i] = max(ed[i], ask(n + m) - ask(vis[i] - 1));
    }
    repd(i, 1, n)
    {
        printf("%d %d\n", be[i], ed[i] );
    }

    return 0;
}

inline void getInt(int* p) {
    char ch;
    do {
        ch = getchar();
    } while (ch == ' ' || ch == '\n');
    if (ch == '-') {
        *p = -(getchar() - '0');
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 - ch + '0';
        }
    }
    else {
        *p = ch - '0';
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 + ch - '0';
        }
    }
}