Educational Codeforces Round 80 (Rated for Div. 2) 题解

  • Deadline
  • Yet Another Meme Problem
  • Two Arrays
  • Minimax Problem
  • Messenger Simulator

Deadline

\[
Time Limit: 2 s\quad Memory Limit: 256 MB
\]
这是个对勾函数,所以最小的话是在 \(sqrt\) 位置,所以只要找这附近的数字就可以了。

view

/***************************************************************
    > File Name        : a.cpp
    > Author           : Jiaaaaaaaqi
    > Created Time     : 2020/1/14 22:35:48
 ***************************************************************/

#include <bits/stdc++.h>
#define  fi         first
#define  se         second
#define  pb         push_back
#define  pii        pair<int, int>
#define  dbg(x)     cout << #x << " = " << (x) << endl
#define  mes(a, b)  memset(a, b, sizeof a)

using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int    maxn = 1e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;

ll n, m;
int T, cas, tol = 0;

int main() {
    // freopen("in", "r", stdin);
    scanf("%d", &T);
    while(T--) {
        scanf("%lld%lld", &n, &m);
        ll x = sqrt(m), ans = INF;
        for(ll i=max(x-1000, 0ll); i<=min(n, x+1000); i++) {
            ans = min(ans, i + (int)ceil(1.0*m/(i+1)));
        }
        puts(ans<=n ? "YES" : "NO");
    }
    return 0;
}

Yet Another Meme Problem

\[
Time Limit: 1 s\quad Memory Limit: 256 MB
\]
以下式子肯定成立
\[
conc(a, b) = a*10^x+b = ab+a+b \\
a*10^x = ab+a \ 10^x = b+1
\]
所以只要满足上述式子的 \(b\) 都是合法的,对 \(a\) 并没有限制,那么就可以算一下有多少个 \(b\) 就可以了。

view

/***************************************************************
    > File Name        : b.cpp
    > Author           : Jiaaaaaaaqi
    > Created Time     : 2020/1/14 22:57:36
 ***************************************************************/

#include <bits/stdc++.h>
#define  fi         first
#define  se         second
#define  pb         push_back
#define  pii        pair<int, int>
#define  dbg(x)     cout << #x << " = " << (x) << endl
#define  mes(a, b)  memset(a, b, sizeof a)

using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int    maxn = 1e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;

ll n, m;
int cas, tol, T;

int main() {
    // freopen("in", "r", stdin);
    scanf("%d", &T);
    while(T--) {
        scanf("%lld%lld", &n, &m);
        ll ans = 0, x = 1;
        for(int i=1; ; i++) {
            x = x*10;
            if(x-1 <= m)    ans++;
            else    break;
        }
        printf("%lld\n", ans*n);
    }
    return 0;
}

Two Arrays

\[
Time Limit: 1 s\quad Memory Limit: 256 MB
\]
令 \(dp[i][j]\) 表示 \(a、b\) 数组都放到第 \(i\) 位,并且 \(b[i]-a[i]=j\) 的方案数,只要保证 \(j>=0\) 就一定是合法的。

view

/***************************************************************
    > File Name        : c.cpp
    > Author           : Jiaaaaaaaqi
    > Created Time     : 2020/1/14 23:04:21
 ***************************************************************/

#include <bits/stdc++.h>
#define  fi         first
#define  se         second
#define  pb         push_back
#define  pii        pair<int, int>
#define  dbg(x)     cout << #x << " = " << (x) << endl
#define  mes(a, b)  memset(a, b, sizeof a)

using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int    maxn = 1e3 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;

int n, m;
int cas, tol, T;

ll dp[11][maxn];

int main() {
    // freopen("in", "r", stdin);
    scanf("%d%d", &n, &m);
    mes(dp, 0);
    dp[0][n-1] = 1;
    for(int i=0; i<m; i++) {
        for(int j=0; j<=n; j++) {
            if(dp[i][j] == 0)   continue;
            ll ans = 1;
            for(int k=j; k>=0; k--) {
                dp[i+1][k] = (dp[i+1][k] + dp[i][j]*ans%mod)%mod;
                ans = (ans+1)%mod;
            }
        }
    }
    ll ans = 0;
    for(int i=0; i<=n-1; i++)   ans = (ans+dp[m][i])%mod;
    printf("%lld\n", ans);
    return 0;
}

Minimax Problem

\[
Time Limit: 5 s\quad Memory Limit: 256 MB
\]
二分答案,然后就变成了判断某个值是否可行的可行性问题。

判断 \(mid\) 是否合法时,令 \(b[i][j] = a[i][j]>=mid ? 1 : 0\),那么问题就变成了找到一对 \(pair<i, j>\) 使得 \(b[i] \& b[j]\) 的二进制全为 \(1\)。那么可以算出每一个行的状态,然后把这个状态的所有子状态都设成 \(i\),然后如果在之前存在某一个状态和这一行的状态互补,则说明找到了一对 \(pair\)。

由于 \(m\) 只有 \(8\),则可以用数组暴力存储。

view

/***************************************************************
    > File Name        : d.cpp
    > Author           : Jiaaaaaaaqi
    > Created Time     : 2020/1/14 23:21:52
 ***************************************************************/

#include <bits/stdc++.h>
#define  fi         first
#define  se         second
#define  pb         push_back
#define  pii        pair<int, int>
#define  dbg(x)     cout << #x << " = " << (x) << endl
#define  mes(a, b)  memset(a, b, sizeof a)

using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int    maxn = 3e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;

int n, m;
int cas, tol, T;

int mx;
int a[maxn][10];
int sta[maxn];

pii check(int mid) {
    for(int i=0; i<=mx; i++)    sta[i] = 0;
    for(int i=1; i<=n; i++) {
        int st = 0;
        for(int j=1; j<=m; j++) {
            if(a[i][j] >= mid)  st |= (1<<(j-1));
        }
        for(int j=st; ; j=(j-1)&st) {
            if(sta[j])  break;
            sta[j] = i;
            if(j==0)    break;
        }
        int nst = mx-st;
        if(sta[nst])    return {i, sta[nst]};
    }
    return {-1, -1};
}

int main() {
    // freopen("in", "r", stdin);
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) for(int j=1; j<=m; j++)
        scanf("%d", &a[i][j]);
    mx = (1<<m)-1;
    int l = 0, r = 1e9;
    pii ans;
    while(l<=r) {
        int mid = l+r>>1;
        pii pa = check(mid);
        if(pa.fi == -1 || pa.se == -1) {
            r = mid-1;
        } else {
            l = mid+1;
            ans = pa;
        }
    }
    printf("%d %d\n", ans.fi, ans.se);
    return 0;
}

Messenger Simulator

\[
Time Limit: 3 s\quad Memory Limit: 256 MB
\]
所有数的最小值和最大值一开始都是 \(i\),然后如果存在某个数字被提取到前面去,则这个数字的最小值就是 \(1\)。

如果某个数字 \(i\) 从来没有被提取到前面去,则其最大值一定是 \(i+\) 出现的大于 \(i\) 的数字种类,这一部分可以使用一个后缀和实现。

对于某个数字 \(i\) 第一次提取到头部时,需要计算这个位置之前的数字中出现了大于 \(i\) 的数字种类,这一部分可以使用树状数组来实现。

对于某个数字 \(i\) 在两次提取到最前之间,也就是位置从 \(1-x-1\) 的过程,那么只要就算这个 \(x\) 即可,这个 \(x\) 其实就是这两次提取到头部之间的数字种类数 \(+1\),这一部分可以使用莫队来实现。为了方便做,对于每个数字的最后一次出现位置可以设置成 \(m+1\)。

view

/***************************************************************
    > File Name        : e.cpp
    > Author           : Jiaaaaaaaqi
    > Created Time     : 2020/1/15 21:07:22
 ***************************************************************/

#include <bits/stdc++.h>
#define  fi         first
#define  se         second
#define  pb         push_back
#define  pii        pair<int, int>
#define  dbg(x)     cout << #x << " = " << (x) << endl
#define  mes(a, b)  memset(a, b, sizeof a)
#define  lowbit(i)  (i&(-i))

using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int    maxn = 1e6 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;

int n, m;
int T, cas, tol = 0;

int block;
struct Node {
    int l, r, id;
    bool operator < (Node a) const {
        return l/block==a.l/block ? r<a.r : l/block<a.l/block;
    }
} node[maxn];
int ans = 0;
int last[maxn], a[maxn];
int Min[maxn], Max[maxn], sum[maxn], cnt[maxn];
bool vis[maxn];

void add(int i) {
    if(cnt[a[i]] == 0)  ans++;
    cnt[a[i]]++;
}

void del(int i) {
    cnt[a[i]]--;
    if(cnt[a[i]] == 0)  ans--;
}

int main() {
    // freopen("in", "r", stdin);
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++) scanf("%d", &a[i]), cnt[a[i]] = 1;
    for(int i=n; i>=1; i--) Min[i] = Max[i] = i, last[i] = m+1, cnt[i] += cnt[i+1];
    block = sqrt(m);
    for(int i=m; i>=1; i--) {
        if(i+1 <= last[a[i]]-1) node[++tol] = {i+1, last[a[i]]-1, a[i]};
        last[a[i]] = i;
    }
    for(int i=1; i<=n; i++) {
        if(last[i] != m+1)  Min[i] = 1;
        else    Max[i] = max(Max[i], i+cnt[i]);
    }
    for(int i=1; i<=m; i++) {
        if(vis[a[i]])   continue;
        for(int j=a[i]; j; sum[j]++, j-=lowbit(j));
        int ans = 0;
        for(int j=a[i]+1; j<=n; ans+=sum[j], j+=lowbit(j));
        Max[a[i]] = max(Max[a[i]], a[i]+ans);
        vis[a[i]] = 1;
    }
    sort(node+1, node+1+tol);
    int L=1, R=0;
    for(int i=1; i<=n; i++) cnt[i] = 0;
    for(int i=1; i<=tol; i++) {
        while(node[i].l < L)    add(--L);
        while(node[i].r > R)    add(++R);
        while(node[i].l > L)    del(L++);
        while(node[i].r < R)    del(R--);
        Max[node[i].id] = max(Max[node[i].id], ans+1);
    }
    for(int i=1; i<=n; i++) printf("%d %d\n", Min[i], Max[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/12199172.html

时间: 2024-10-09 09:07:27

Educational Codeforces Round 80 (Rated for Div. 2) 题解的相关文章

Educational Codeforces Round 80 (Rated for Div. 2)

\[Educational\ Codeforces\ Round\ 80\ (Rated\ for\ Div.\ 2)\] A.Deadline 打勾函数找最小值,在\(\sqrt{d}\)邻域里找\(x\)最小化\(x+\lceil\frac{d}{x+1}\rceil\)即可 //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace

Educational Codeforces Round 36 (Rated for Div. 2) 题解

Educational Codeforces Round 36 (Rated for Div. 2) 题目的质量很不错(不看题解做不出来,笑 Codeforces 920C 题意 给定一个\(1\)到\(n\)组成的数组,只可以交换某些相邻的位置,问是否可以将数组调整为升序的 解题思路 首先如果每个数都能通过交换到它应该到的位置,那么就可以调整为升序的. 但实际上交换是对称的,如果应该在的位置在当前位置前方的数都交换完成,那么整体就是排好序的,因为不可能所有不在相应位置的数都在相应位置的后方.

Educational Codeforces Round 80 (Rated for Div. 2)(C - Two Arrays )

C - Two Arrays 题目链接:https://codeforces.com/contest/1288/problem/C 题目: 题意:给你n,m,利用1~n之间的数(可重复)来组成长度为m的数组a,b,要求数组a非递减,数组b非递增,且a数组的数<=b数组中的数,求出a,b数组对数 思路:用动态规划,dp[i][j]是第i个位置放数字j的方案数,根据题意可以将b数组反置然后接在a后面,则该数组长度为2m,为一个非递减序列,则就是求1~n这些数字可重复组成多少种长度为2m的非递减序列,

Educational Codeforces Round 80 (Rated for Div. 2)参加感悟

这次比赛有14000+的人报名,结果我得了266名,创了新纪录. 进过这次比赛,我有回答了1800+. 寒假到了,又可以每次比赛都打了.平时进步很慢,我希望能在寒假有更大的进步. 作为寒假第一场比赛,发挥让我还是很满意的. 开始讲题: A: http://codeforces.com/contest/1288/problem/A 这题太水了,直接是sqrt(d)-1和sqrt(d),如果它们不行,那么其他的也肯定不行. 直接上代码: 1 #include<bits/stdc++.h> 2 #d

Educational Codeforces Round 80 (Rated for Div. 2(A Deadline )

(A) Deadline 题目: 思路:一开始还傻傻的暴力康康....只要求出令x=n的一半就行,然后判断 1 #include<bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 //freopen("text","r",stdin); 6 int T; 7 scanf("%d",&T); 8 while(T--) 9 { 10 //cout<<cei

题解 Educational Codeforces Round 80 [Rated for Div. 2](CF1288)

前言:11点的时候某天下第一可爱的萌神问我怎么不打CF,跑去开题,11:30终于开了C和D,秒了一下,考后萌神轻松上分并告诉我E的tag于是我赛后补题. A:n/x上取整是(n-1)/x+1,式子变形成x+1+(n-1)/(x+1)<=d.根据a+b>=2√ab随便化简一下.(20s秒了??) 1 #include<stdio.h> 2 #include<math.h> 3 using namespace std; 4 int T,n,d,x,y; 5 int main

Educational Codeforces Round 80 (Rated for Div. 2) C - Two Arrays(DP)

???♀? ???♀? ???♀? 题意:从1~n里面选出来m个数字组成a数组,再选出来m个组成b数组,要求a非递减,b非递增,且bi>=ai 1,说是选两个数组其实就是选出来一个长m*2的非递减数组 2,假设要从n的全排列中选出来m长的非递减数组,因为元素是可重复的,最多重复m次,其实就是相当于从下面这个矩阵中选择元素 从这个矩阵中选择元素,每行只能选择一个,枚举我们选出的k个元素的最小值为[ i , j ]位置,那么除去这个元素选择k-1个元素的方案数之和就是k个元素,如图中红色标出位置,最

Educational Codeforces Round 80 (Rated for Div. 2)C(DP)

1 #define HAVE_STRUCT_TIMESPEC 2 #include<bits/stdc++.h> 3 using namespace std; 4 const long long mod = 1e9+7; 5 long long pre[1007][1007],temp[1007][1007]; 6 int main(){ 7 ios::sync_with_stdio(false); 8 cin.tie(NULL); 9 cout.tie(NULL); 10 int n,m;

Educational Codeforces Round 80 (Rated for Div. 2)部分题解

A. Deadline 题目链接 题目大意 给你\(n,d\)两个数,问是否存在\(x\)使得\(x+\frac{d}{x+1}\leq n\),其中\(\frac{d}{x+1}\)向上取整. 解题思路 方案一:利用均值不等式公式推导 \(x+\frac{d}{x+1}=x+1+\frac{d}{x+1}-1\geq2\sqrt{d}-1\) 所以 \(\min(x+\frac{x}{d+1})=2\sqrt{d}-1\) 因此去判断\(2\sqrt{d}-1\leq n\)是否成,即\(4\