CF1288题解

CF1288

Educational Codeforces Round 80 (Rated for Div. 2)

A略

CF1288B

link

CF1288B题意

  • 给定 \(A,B\),问有多少个二元组 \((a,b)\) 满足 \(1\le a \le A,1\le b\le B\)\(a\cdot b+a+b=\operatorname{conc}(a,b)\)。其中 \(\operatorname{conc}(a,b)\) 表示将 \(a,b\) 拼接在一起,比如 \(\operatorname{conc}(12,23)=1223,\operatorname{conc}(100,11)=10011\)\(a,b\) 不能含有前导 \(0\)

  • 本题有多组数据。令数据组数为 \(T\)\(T\le 100\)\(A\le 10^9\)\(B\le 10^9\)

CF1288B题解

不妨设 \(b\) 的位数是 \(count(b)\)
那么有

\[a\times(b+1)+b=a\times 10^{count(b)}+b\\ \iff a\times(b+1)=a\times 10^{count(b)}\\ \iff b+1=10^{count(b)} \]

发现这个式子与 \(a\) 无关,成立当且仅当 \(b=10^k-1\)
不难发现这样的 \(b\) 形如 \(9,99,999,\dots,999\dots999\)(设成立的总数是 \(f(b)\))。
而只要 \(b\) 满足了,\(a\) 是什么都无所谓,答案就是 \(a\times f(b)\)
没了。

CF1288B代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
signed main(){
int T = read();
while(T--){
    int a = read(), b = read();
    int cnt = 0,tmp = 1;
    while(tmp - 1 <= b){tmp *= 10;cnt++;}
    if(cnt)cnt--;
    printf("%lld\n",a * cnt);
}
    return 0;
}

CF1288C

link

CF1288C题意

输入两整数 \(n\)\(m\),求有多少对正整数序列 \(a_i\)\(b_i\) 满足:

  • \(a_i\)\(b_i\) 的长度为 \(m\)
  • \(a_i\)\(b_i\) 中所有元素的值小于等于 \(n\)
  • 对于所有 \(1\le i\le m\)\(a_i\le b_i\)
  • 序列 \(a\) 单调不降,序列 \(b\) 单调不升。

答案对 \(10^9+7\) 取模。

CF1288C题解

首先将 \(b\) 序列左右对称一下,然后接到 \(a_m\) 后面,不难发现,原题转化为求值域是 \([1,n]\),长度是 \(2\times m\) 的序列的数量。
简单DP即可。

CF1288C代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f =  -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1e3 + 10;
const ll mod = 1e9 + 7;
int n, m;
ll f[42][maxn];
signed main(){
    n = read(); m = read();
    for(int i = 1;i <= n;i++)f[1][i] = 1;
    for(int i = 2;i <= m * 2;i++){
        ll sum = 0;
        for(int j = 1;j <= n;j++){
            // printf("%d %d\n",i,j);
            sum += f[i - 1][j];
            if(sum > mod)sum -= mod;
            f[i][j] = sum;
        }
    }
    ll sum = 0;
    for(int i = 1;i <= n;i++){
        sum += f[m * 2][i];if(sum > mod)sum -= mod;
        // printf("%lld\n",f[m * 2][i]);
    }
    printf("%lld\n",sum);
    return 0;
}

CF1288D

link

CF1288D题意

给出一个 \(n\)\(m\) 列的数字矩阵 \(a\),找出两行 \(x, y\),令 \(b_j = \max(a_{x, j}, a_{y, j})\),试使得 \(\min\limits_{1 \le j \le m}b_j\) 最大,输出选择的 \(x, y\),可以相同。

CF1288D题解

二分最大的 \(\min\limits_{1 \le j \le m}b_j=x\),然后考虑怎么验证答案 \(x\) 是否合法。
先将矩阵中所有 \(a_{i,j}\ge x\) 的标记为 \(1\),否则标记为 \(0\)
如果将每一行(别忘了 \(m\le8\))都压缩成二进制,那么问题就转变成找到两行,使得 \(or\) 值是 \(2^m-1\)
这东西想怎么做怎么做,DP,dfs,随意。

CF1288D代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 3e5 + 10;
int a[maxn][8];
int n, m;int f[1 << 8];
int x,  y;
bool check(int mid){
    memset(f,0,sizeof(f));
    for(int i = 1;i <= n;i++){
        int sta = 0;
        for(int j = 0;j < m;j++)
            if(a[i][j] >= mid)sta |= (1 << j);
        if(sta == (1 << m) - 1){x = y = i;return true;}
        for(int sta1 = sta;sta1;sta1 = sta & (sta1 - 1))
            if(f[sta1]){x = f[sta1];y = i;return true;}
        f[((1 << m) - 1) ^ sta] = i;
    }
    // printf("mid = %d\n",mid);
    // for(int i = 0;i < (1 << m);i++)printf("sta = %d,f = %d\n",i,f[i]);
    return false;
}
signed main(){
    n = read(); m = read(); int mx = 0;
    for(int i = 1;i <= n;i++)
        for(int j = 0;j < m;j++)
            mx = max(mx,a[i][j] = read());
    int l = 0, r = mx, ans = 0;
    while(l <= r){
        int mid = l + r >> 1;
        if(check(mid)){l = mid + 1;ans = mid;}
        else r = mid - 1;
    }
    check(ans);
    printf("%d %d\n",x, y);
    return 0;
}

CF1288E

link

CF1288E题意

一个长度为 \(n\) 的好友列表,自上而下依次是 \(1 \sim n\),你依次收到了 \(m\) 条消息,第 \(i\) 条消息是 \(a_i\) 发来的,这时 \(a_i\) 会跳到会话列表的最上面,其它的按原顺序顺延,求 \(1 \sim n\) 每个好友最靠上的位置和最靠下的位置。

CF1288E题解

很清新的DS题。
维护 \(n+m\) 个位置,每个位置表示有没有数字,然后每次更新答案只会更新调整位置的那个数,想怎么维护怎么维护。
有可能有的数还没被移动,没有更新,最后在扫一遍即可。

CF1288E代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 6e5 + 10;
int n, m;
int ansl[maxn],ansr[maxn], pos[maxn];
struct BIT{
    int c[maxn << 2];
    inline int lowbit(int x){return x & (-x);}
    void upd(int x,int upd){for(;x <= n + m;x += lowbit(x))c[x] += upd;}
    int qry(int x){int ans = 0;for(;x;x -=lowbit(x))ans += c[x];return ans;}
}tree;
signed main(){
    n =  read(); m = read();
    for(int i = 1;i <= n;i++){
        ansl[i] = ansr[i] = i;
        pos[i] = i + m;
        tree.upd(i + m,1);
    }
    for(int i = 1;i <= m;i++){
        int u = read();ansl[u] = 1;
        ansr[u] = max(ansr[u],tree.qry(pos[u]));
        tree.upd(pos[u],-1);pos[u] = m - i + 1;
        tree.upd(pos[u],1);
    }
    for(int i = 1;i <= n;i++){ansr[i] = max(ansr[i],tree.qry(pos[i]));}
    for(int i = 1;i <= n;i++)printf("%d %d\n",ansl[i],ansr[i]);
    return 0;
}
posted @ 2023-12-03 08:25  Call_me_Eric  阅读(44)  评论(0)    收藏  举报
Live2D