CF1207

博客围绕CF1207系列题目展开,涵盖A - F六道题。介绍了各题题意,如A题求餐厅卖汉堡的最大收益,B题是对01矩阵操作。还给出题解,涉及贪心、动态规划、容斥原理等算法,最后附上各题代码。

CF1207

CF1207A

link

CF1207A题意

在你的餐厅里有两种汉堡:牛肉汉堡和鸡肉汉堡。每个牛肉汉堡需要 2 2 2片面包和 1 1 1片牛肉,一个鸡肉汉堡需要 2 2 2片面包和 1 1 1个鸡排。

一个牛肉汉堡卖 h h h元,一个鸡肉汉堡卖 c c c元。

你有 b b b片面包, p p p片牛肉和 f f f块鸡排。求最大收益。

CF1207A题解

可以发现肯定尽可能发现选单价多的问题。

CF1207A代码

#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;
}
int b, p, f, h, c;
signed main(){
int T = read();
while(T--){
    b = read(); p = read(); f = read();
    h = read(); c = read();
    if(h < c){swap(h, c);swap(p, f);}
    int a = min(b / 2, p);
    int ans = a * h;
    ans += min((b - 2 * a) / 2,f) * c;
    printf("%d\n",ans);
}
    return 0;
}

CF1207B

link

CF1207B题意

给定一个 n × m ( n , m ≤ 50 ) n\times m(n,m\le 50) n×m(n,m50) 的01目标矩阵和一个全是0的原始矩阵,每次可以对点 ( i , j ) ( i < n , j < m ) (i,j)(i<n,j<m) (i,j)(i<n,j<m) 进行操作,使得原始矩阵中 ( i , j ) , ( i + 1 , j ) , ( i , j + 1 ) , ( i + 1 , j + 1 ) (i,j),(i + 1,j),(i,j + 1),(i + 1,j + 1) (i,j),(i+1,j),(i,j+1),(i+1,j+1) 的位置改成1。不要求尽可能少操作,只要求给出一个解法即可。

CF1207B题解

不要求最少操作,那就能操作就操作呗:)
具体而言,如果目标矩阵中 ( i , j ) , ( i + 1 , j ) , ( i , j + 1 ) , ( i + 1 , j + 1 ) (i,j),(i + 1,j),(i,j + 1),(i + 1,j + 1) (i,j),(i+1,j),(i,j+1),(i+1,j+1) 都是1,那就操作 ( i , j ) (i,j) (i,j)
最后判一下能不能行就好了。

CF1207B代码

#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 = 51;
int a[maxn][maxn],b[maxn][maxn],n, m;
bool book[maxn][maxn];
vector<pair<int,int> > vec;
signed main(){
    n = read(); m = read();
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            a[i][j] = read();
    for(int i = 0;i <= n;i++)a[i][0] = 1;
    for(int i = 0;i <= m;i++)a[0][i] = 1;
    for(int i = 2;i <= n;i++)
        for(int j = 2;j <= m;j++)
            if(a[i][j] && a[i - 1][j] && a[i][j - 1] && a[i - 1][j - 1]){
                book[i][j] = 1;
            }
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            if(book[i][j]){
                b[i][j] = b[i - 1][j] = b[i][j - 1] = b[i - 1][j - 1] = 1;
            }
    int ans = 0;
    for(int i = 1;i <= n && ans != -1;i++)
        for(int j = 1;j <= m;j++){
            if(a[i][j] != b[i][j]){ans = -1;break;}
            if(book[i][j]){ans++;vec.push_back(make_pair(i,j));}
        }
    if(ans != -1){
        printf("%d\n",ans);
        for(auto i : vec){printf("%d %d\n",i.first - 1,i.second - 1);}
    }
    else puts("-1");
    return 0;
}

CF1207C

link

CF1207C题意

你需要在城市里修建管道和支柱,管道和支柱的单位长度的价格分别为 a , b a,b a,b

给你一个长度为 n n n 01 01 01序列,其中 1 1 1表示这里需要通车, 0 0 0表示这里不需要通车,高度为 2 2 2的地方才可以通车(保证序列的头尾不需要通车)

1

如图,红色表示管道,黑色表示支柱,我们可以在一段单位区间建立一条S形线条,每个S形可以表示为该段由三部分组成: 0.5 0.5 0.5个单位的水平管道+ 1 1 1个单位的垂直管道+ 0.5 0.5 0.5个单位的水平管道

每一段的单位区间左右都有支柱支撑,支柱的高度等于管道端点的高度

你需要保证所有通车的地方高度为 2 2 2的同时,建管道和支柱的费用总和最小

CF1207C题解

简单DP那你还调半天?!
f i , 0 / 1 f_{i,0/1} fi,0/1 表示第 i i i 个支柱是高度为1还是2的最小值。
于是就有

  • f i , 0 = min ⁡ ( f i − 1 , 0 + a + b , f i − 1 , 1 + 2 a + b ) f_{i,0} = \min(f_{i-1,0} + a + b,f_{i-1,1}+2a+b) fi,0=min(fi1,0+a+b,fi1,1+2a+b)
  • f i , 1 = min ⁡ ( f i − 1 , 0 + 2 a + 2 b , f i − 1 , 1 + a + 2 b ) f_{i,1} = \min(f_{i-1,0} + 2a + 2b,f_{i-1,1}+a+2b) fi,1=min(fi1,0+2a+2b,fi1,1+a+2b)

然后注意题意,题中说的是i~i+1的高度,不是第i个支柱的高度,故\

  • f i , 0 = I N F ( s i = = 1 ∣ ∣ s i + 1 = = 1 ) f_{i,0} = INF(s_i==1 || s_{i + 1} == 1) fi,0=INF(si==1∣∣si+1==1)

没了。

CF1207C代码

#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;
}
const int maxn = 2e5 + 10;
int f[maxn][2];
int n, a, b;
char ch[maxn];
signed main(){
int T = read();
while(T--){
    memset(f,0x3f,sizeof f);
    n = read(); a = read(); b = read();
    scanf("%s",ch + 1);f[0][0] = b;
    for(int i = 1;i <= n;i++){
        f[i][0] = min(f[i - 1][0] + a + b,f[i - 1][1] + 2LL * a + b);
        f[i][1] = min(f[i - 1][0] + 2LL * a + 2LL * b,f[i - 1][1] + a + 2LL * b);
        if(ch[i] == '1' || ch[i + 1] == '1')f[i][0] = 0x3f3f3f3f3f3f3f3f;
    }
    // for(int i = 1;i <= n;i++){printf("i=%lld:%lld %lld\n",i,f[i][0],f[i][1]);}
    printf("%lld\n",f[n][0]);
}
    return 0;
}

CF1207D

link

CF1207D题意

n ( n ≤ 3 × 1 0 5 ) n(n\le3\times10^5) n(n3×105) 个二元组 ( a i , b i ) (a_i,b_i) (ai,bi), 问排列后第一元或第二元不按照非严格升序排列的排列方式有多少种。答案对998244353取模。

CF1207D题解

简单容斥+计数
经典trick:第一元或第二元不按照非严格升序排列 ⇔ \Leftrightarrow 总排列数-第一元按照非严格升序排列-第二元按照非严格升序排列+第一元第二元同时按照非严格升序排列。
然后注意一下可能加一次998244353不能为正,需要加好几次。吃我一次提交
没了。

CF1207D代码

#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;
}
const int maxn = 3e5 + 10, mod = 998244353;
int n;
struct node{
    int first, second;
    node(int f = 0,int s = 0):first(f),second(s){}
}a[maxn];
int typ, pw[maxn];
bool cmp(node a,node b){
    if(typ == 1){return a.first < b.first;}
    if(typ == 2){return a.second < b.second;}
    if(typ == 3){return a.first != b.first ? a.first < b.first : a.second < b.second;}
}

int calc(int type){
    typ = type; sort(a + 1,a + 1 + n,cmp);
    int len = 1, times = 1;
    // printf("type = %lld\n",type);
    // for(int i = 1;i <= n;i++)printf("%lld %lld\n",a[i].first,a[i].second);
    for(int i = 1;i <= n && times != 0;i++){
        switch(type){
            case 1:{
                if(a[i].first == a[i - 1].first){len++;}
                else {times = times * pw[len] % mod;len = 1;}
                break;
            }
            case 2:{
                if(a[i].second == a[i - 1].second){len++;}
                else {times = times * pw[len] % mod;len = 1;}
                break;
            }
            case 3:{
                if(a[i].second < a[i - 1].second){times = 0;}
                if(a[i].first == a[i - 1].first && a[i].second == a[i - 1].second){len++;}
                else {times = times * pw[len] % mod;len = 1;}
                break;
            }
            default:break;
        }
    }
    return times * pw[len] % mod;
}

signed main(){
    n = read(); int u, v, ans = 1;pw[0] = 1;
    for(int i = 1;i <= n;i++){
        pw[i] = pw[i - 1] * i % mod;
        u = read(); v = read();
        a[i] = node(u, v);
    }
    int tmp = (pw[n] - calc(1) - calc(2) + calc(3) + mod) % mod;
    while(tmp < 0)tmp += mod;
    printf("%lld\n",tmp);
    return 0;
}

CF1207E

link

CF1207E题意

交互题
现在有一个在 [ 0 , 2 14 − 1 ] [0,2^{14}-1] [0,2141] 的数字 x x x,只让你问两次,每次你需要恰好给出 100 个不能重复的数字。
交互库会随机选一个数 a i a_i ai,返回你 x ⊕ a i x\oplus a_i xai 的值。

CF1207E题解

这啥啊
不难想到 a i ⊕ x ⊕ b j ⊕ x = a i ⊕ b j a_i\oplus x\oplus b_j\oplus x=a_i\oplus b_j aixbjx=aibj
现在想构造两个序列,使得对于一个异或值 t m p tmp tmp,最多只有一组 ( i , j ) (i,j) (i,j) 满足 t m p = a i ⊕ b j tmp=a_i\oplus b_j tmp=aibj
不难想到,让 a ∈ [ 1 , 100 ] a\in[1,100] a[1,100] b ∈ [ 1 < < 7 , 100 < < 7 ] b\in[1<<7,100<<7] b[1<<7,100<<7],这样一定不会有重复。
然后就没了。

CF1207E代码

#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 mod = (1 << 14);
vector<int> vec1, vec2;
bool book[mod];
signed main(){
    for(int i = 1;i <= 100;i++)vec1.push_back(i);
    for(int i = 1;i <= 100;i++)vec2.push_back(i << 7);
    cout << "?"; for(int i : vec1){cout << ' ' << i;}cout << endl;
    int tmp1;cin >> tmp1;
    cout << "?"; for(int i : vec2){cout << ' ' << i;}cout << endl;
    int tmp2;cin >> tmp2;
    int x = 0, y = 0;bool flag = 0;
    for(int i : vec1){
        if(flag)break;
        for(int j : vec2)
            if((i ^ j) == (tmp1 ^ tmp2)){
                flag = 1;x = i,y = j;break;
            }
    }
    cout << "! " << (tmp1 ^ x) << endl;
    return 0;
}

CF1207F

link

CF1207F题意

给你一个长度为 500000 500000 500000 的序列,初值为 0 0 0 ,你要完成 q q q 次操作,操作有如下两种:

  1. 1 x y : 将下标为 x x x 的位置的值加上 y y y
  2. 2 x y : 询问所有下标模 x x x 的结果为 y y y 的位置的值之和

CF1207F题解

根号分治模板题。
先想两种暴力方式。

  1. 每次强行在原序列加,询问时每次暴力跳 x x x,时间复杂度 O ( n 2 x ) O(\frac{n^2}{x}) O(xn2),修改时直接改 a x a_x ax,时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( n ) O(n) O(n)
  2. 预处理 f i , j f_{i,j} fi,j 表示下标模 i i i j j j 的位置的值之和。每次询问时直接输出预处理结果即可,时间复杂度 O ( 1 ) O(1) O(1),修改时枚举1~x的模数,暴力修改即可,时间复杂度 O ( n x ) O(nx) O(nx),空间复杂度 O ( n x ) O(nx) O(nx)

不难发现,这两个暴力可以捏在一起,然后设置一个阈值 l i m lim lim,当 x > l i m x>lim x>lim 时跑第一个暴力, x ≤ l i m x\le lim xlim 时跑第二个暴力。
l i m = n lim=\sqrt n lim=n 时时空复杂度都是最优的 O ( n n ) O(n\sqrt{n}) O(nn )

CF1207F代码

#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 - '0'; ch = getchar();}
    return x * f;
}
const int maxn = 500000 + 10, maxm = 1e3 + 10;
int n, q, len;
int a[maxn];
int dp[maxm][maxm];
signed main(){
    int opt, x, y;
    n = 5e5; q = read(); len = sqrt(n);
    while(q--){
        opt = read(); x = read(); y = read();
        if(opt == 1){
            a[x] += y;
            for(long long i = 1;i <= len;i++){
                dp[i][x % i] += y;
            }
        }
        else{
            if(x <= len)printf("%lld\n",dp[x][y % x]);
            else{
                int ans = 0;
                for(long long i = y % x;i <= n;i += x)ans += a[i];
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}
# CF1207D Number Of Permutations ## 题目描述 给定一个长度为 $n$ 的整数对序列:$ (a_1, b_1), (a_2, b_2), \dots , (a_n, b_n) $。如果该序列按第一个元素非递减排序,或者按第二个元素非递减排序,则称该序列为“坏序列”;否则称为“好序列”。下面是一些好序列和坏序列的例子: - $ s = [(1, 2), (3, 2), (3, 1)] $ 是坏序列,因为第一个元素序列 $ [1, 3, 3] $ 已经有序; - $ s = [(1, 2), (3, 2), (1, 2)] $ 是坏序列,因为第二个元素序列 $ [2, 2, 2] $ 已经有序; - $ s = [(1, 1), (2, 2), (3, 3)] $ 是坏序列,因为两个元素序列(第一个元素序列和第二个元素序列)都已经有序; - $ s = [(1, 3), (3, 3), (2, 2)] $ 是好序列,因为无论第一个元素序列 $ ([1, 3, 2]) $ 还是第二个元素序列 $ ([3, 3, 2]) $ 都没有有序。 请计算有多少个长度为 $n$ 的排列,使得将该排列作用于序列 $s$ 后,得到的是一个好序列。 一个长度为 $n$ 的排列 $p$ 是一个由 $1$ 到 $n$ 的 $n$ 个不同整数组成的序列 $p_1, p_2, \dots, p_n$($1 \le p_i \le n$)。如果将排列 $p_1, p_2, \dots, p_n$ 作用于序列 $s_1, s_2, \dots, s_n$,则得到序列 $s_{p_1}, s_{p_2}, \dots, s_{p_n}$。例如,如果 $s = [(1, 2), (1, 3), (2, 3)]$ 且 $p = [2, 3, 1]$,则 $s$ 变为 $[(1, 3), (2, 3), (1, 2)]$。 ## 输入格式 第一行包含一个整数 $n$($1 \le n \le 3 \cdot 10^5$)。 接下来的 $n$ 行描述序列 $s$。第 $i$ 行包含两个整数 $a_i$ 和 $b_i$($1 \le a_i, b_i \le n$),分别表示第 $i$ 个数对的第一个和第二个元素。 序列 $s$ 可能包含相同的元素对。 ## 输出格式 输出使得作用排列后序列 $s$ 变为好序列的排列数量。答案对 $998244353$ 取模。 ## 输入输出样例 #1 ### 输入 #1 ``` 3 1 1 2 2 3 1 ``` ### 输出 #1 ``` 3 ``` ## 输入输出样例 #2 ### 输入 #2 ``` 4 2 3 2 2 2 1 2 4 ``` ### 输出 #2 ``` 0 ``` ## 输入输出样例 #3 ### 输入 #3 ``` 3 1 1 1 1 2 3 ``` ### 输出 #3 ``` 4 ``` ## 说明/提示 在第一个测试用例中,长度为 $3$ 的排列共有六种: 1. 如果 $p = [1, 2, 3]$,则 $s = [(1, 1), (2, 2), (3, 1)]$ —— 坏序列(按第一个元素有序); 2. 如果 $p = [1, 3, 2]$,则 $s = [(1, 1), (3, 1), (2, 2)]$ —— 坏序列(按第二个元素有序); 3. 如果 $p = [2, 1, 3]$,则 $s = [(2, 2), (1, 1), (3, 1)]$ —— 好序列; 4. 如果 $p = [2, 3, 1]$,则 $s = [(2, 2), (3, 1), (1, 1)]$ —— 好序列; 5. 如果 $p = [3, 1, 2]$,则 $s = [(3, 1), (1, 1), (2, 2)]$ —— 坏序列(按第二个元素有序); 6. 如果 $p = [3, 2, 1]$,则 $s = [(3, 1), (2, 2), (1, 1)]$ —— 好序列。 c++
09-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值