CF821题解

CF821

Codeforces Round 420 (Div. 2)

CF821A

link

CF821A题意

Okabe要改进他的实验室。实验室用一个 \(n\times n\) 的正方形网格表示(\(n\) 为正整数)。他认为,一个“好实验室”的网格内每一个不等于 \(1\) 的数字都可以用同一行和同一列的某个数字之和表示。换句话说,对于任意 \(x,y\)\(1 \le x,y \le n\)\(a_{x, y} \neq 1\)),存在两个数 \(s\)\(t\),使得\(a_{x, y} = a_{x, s} + a_{t, y}\),其中\(a_{i, j}\) 表示第 \(i\) 行第 \(j\) 列的整数。

帮助Okabe找出以下的实验室中哪个符合他的要求。

CF821A题解

暴力枚举即可

CF821A代码

#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 = 100;
int n;
int a[maxn][maxn];
signed main(){
    n = read();
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            a[i][j] = read();
    for(int i = 1;i <= n;i++)
    for(int j = 1;j <= n;j++)
        if(a[i][j] != 1){
            bool check = false;
            for(int s = 1;s <= n && !check;s++)
            for(int t = 1;t <= n;t++){
                if(a[i][s] + a[t][j] == a[i][j]){
                    check = true;break;
                }
            }
            if(!check){puts("No");return 0;}
        }
    puts("YEs");
    return 0;
}

CF821B

link

CF821B题意

由于某种奇怪的原因,Okabe需要香蕉来进行自己的实验。于是他决定去森林砍香蕉树。
考虑在二维平面上的整点 \((x,y)\),其中 \(0\le x,y\)\(x,y\) 是整数。在这个整点上有一棵拥有 \(x+y\) 个香蕉的香蕉树。其余不满足上述条件的点没有香蕉树。
现在,Okabe画了一条直线 \(y=-\frac{x}{m}+b\)Okabe可以选择直线上一个点 \((a,b)\),作出以 \((0,0)\) 为左下角,\((a,b)\) 为右上角的矩形。Okabe可以取走所有在矩形内和在矩形边界上的点的香蕉。需要注意的是,这个矩形可以是一条线甚至是一个点。
现在给出Okabe画出的直线,请求出他在只选择一个矩形的前提下能够取走最多的香蕉的数量。
\(m\le10^3,b\le10^4\),保证答案不会超过 \(10^{18}\)

CF821B题解

先推导下柿子:

\[y=-\frac{x}{m}+b\\ \iff y\times m=-x+b\times m\\ \iff x=(b-y)\times m \]

然后发现,当且仅当 \(y\) 是整数的时候,\((x,y)\) 是整点。
然后观察题意,发现,以整点为右上角做矩形一定比不是整点的更优,因为你可以通过调整一个以不是整点为右上角的矩形到整点,不但能够包含之前的点,还能增加在边界上的点。
发现 \(y\) 很小,可以直接暴力枚举。
对于一个以 \((x,y)\) 为右上角的矩形,考虑答案怎么算。
将一个点的贡献 \(x+y\) 拆成两个,变成 \(x\)\(y\)
然后就很好做,答案就是 \(\frac{(x+1)\times(y+1)\times y}{2}+\frac{(x+1)\times(y+1)\times x}{2}\)

CF821B代码

#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;
}
int m, b;
signed main(){
    m = read(); b = read();
    int ans = 0;
    for(int y = 0;y <= b;y++){
        int x = m * (b - y);
        ans = max(ans,(x + 1) * y * (y + 1) / 2 + (x + 1) * x * (y + 1) / 2);
    }
    printf("%lld\n",ans);
    return 0;
}

CF821C

link

CF821C题意

OkabeDaru正在向一个栈中加入和删除盒子。盒子编号从 \(1\)\(n\),最开始栈中没有盒子。
作为一个控制狂,Okabe给了Daru \(2\times n\) 个命令:\(n\) 个命令让他将某一个盒子加入栈中, \(n\) 个命令让他弹出栈顶。Okabe希望弹出栈顶的盒子顺序是从 \(1\)\(n\)。当然,这意味着,Daru可能在他的命令下做不到按顺序弹出。
但是Daru可以在两个命令之间调整整个栈中的盒子顺序。当然,这个时候他不能执行命令。
Daru想问你最少的调整次数。保证一个盒子在需要被弹出之前一定被加入过。
\(n\le3\times10^5\)

CF821C题解

先想暴力:
每次遇到remove的时候如果栈顶不是想要的就 \(sort\) 一下,然后计数器加一。
显然会TLE,怎么办?
每次需要 \(sort\) 的时候,直接弹出所有元素,然后栈空且需要弹出的时候就当这个元素已经弹出了。
为什么是对的?
因为每次排序一定会贪心的排所有元素的顺序,但是显然已经排好序后,如果后来的元素仍然有序,那么就一定不需要排序,而后来的无序的话,我们又一定会再进行一次这个贪心,答案就一定是对的。

CF821C代码

#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 st[maxn], top;
int n, pos;
signed main(){
    scanf("%d",&n);
    char opt[10];int u, ans = 0;pos = 0;
    for(int i = 1;i <= n * 2;i++){
        scanf(" %s",opt);
        if(opt[0] == 'a'){
            scanf("%d",&u);
            st[++top] = u;
        }
        else{
            pos++;
            if(top == 0)continue;
            if(st[top] != pos){
                ans++;top = 0;
            }
            else top--;
        }
    }
    printf("%d\n",ans);
    return 0;
}

CF821D

link

CF821D题意

Okabe喜欢沿着有路灯的小路穿行城市。但是如果沿途的路灯没有点亮,在一片漆黑中,他就会被他那些跑得超级快的小伙伴抛弃。

Okabe居住的苟城可以用一个二维网格表示。行从上到下编为 \(1\sim N\),列从左到右编为 \(1\sim N\)。有K盏路灯是亮着的。初始时左上角的路灯已点亮。

Okabe开始了他的旅程。他从左上角出发,目的地是右下角。当然,Okabe只会经过点亮的灯,而且只能向相邻的上、下、左、右单元走。然而,Okabe也可以支出1s的寿命来暂时点亮某一行或某一列的灯,从而通过一些最初没有点亮的区域。

值得注意的是,Okabe在同一时间内只能点亮某一行或某一列的路灯,且在每次点亮新的行列时都必须重新 \(-1s\)。要更改临时点亮的行和列时,他必须站在一开始就亮着的地方。而当他更改时,被点亮的一整行或一整列灯会同时熄灭。

由于Okabe的寿命是有限的,请帮助Okabe找出最少需要支出多少寿命来完成这次旅行。

输入 输入的第一行包括三个整数\(n,m,k\),中间用空格隔开。(\(2 \le n, m, k \le 10^4\)).

接下来的k行,每行有两个整数 \(r_i\)\(c_i\) (\(1 \le ri \le n, 1 \le ci \le m\)) ,表示初始时已经点亮的灯的行和列。

输入保证每个被点亮的灯的坐标不重合,且左上角的灯一定是亮的。

输出

输出Okabe到达目的地需要支出的最少寿命。如果不可能到达,输出-1。

CF821D题解

逆天翻译
不难想到,有一维距离不超过 \(2\) 的一对点可以以 \(1\) 花费到达,相邻的可以 \(0\) 费到达,剩下的到不了。
实际实现的时候不需要连边(太多了会RE),直接暴力枚举即可。

CF821D代码

#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 n, m, k;

struct hash_function{size_t operator ()(pair<int,int> a) const{return (a.first) * (m + 2) + (a.second);}};
map<pair<int,int>,int> mp;
int cnt, x[maxn], y[maxn];

struct node{
    int first, second;
    node(int f = 0,int s = 0):first(f),second(s){}
    friend bool operator < (node a,node b){return a.first != b.first ? a.first < b.first : a.second < b.second;}
    friend bool operator > (node a,node b){return a.first != b.first ? a.first > b.first : a.second > b.second;}
};

int dis[maxn << 1];bool book[maxn << 1];
priority_queue<node,vector<node>,greater<node> > que;
vector<int> line[maxn], row[maxn];
signed main(){
    n = read();m = read();k = read();
    for(int i = 1;i <= k;i++){
        x[i] = read();y[i] = read();
        mp[make_pair(x[i],y[i])] = ++cnt;
        line[x[i]].push_back(cnt);
        row[y[i]].push_back(cnt);
    }
    int S = mp[make_pair(1,1)], T = mp[make_pair(n, m)];
    memset(dis,0x3f,sizeof(dis));dis[S] = 0;que.push(node(0,S));
    while(!que.empty()){
        int u = que.top().second;que.pop();
        if(book[u])continue;book[u] = 1;
        vector<int> to1,to0;to1.clear();to0.clear();
        int i = u;
        int pos = mp[make_pair(x[i],y[i])];
        for(int j : line[x[i]]){to1.push_back(j);}
        for(int j : row[y[i]]){to1.push_back(j);}
        for(int j : line[x[i] + 1]){to1.push_back(j);}
        for(int j : line[x[i] - 1]){to1.push_back(j);}
        for(int j : row[y[i] + 1]){to1.push_back(j);}
        for(int j : row[y[i] - 1]){to1.push_back(j);}
        if(x[i] > 1)for(int j : line[x[i] - 2]){to1.push_back(j);}
        if(y[i] > 1) for(int j : row[y[i] - 2]){to1.push_back(j);}
        for(int j : line[x[i] + 2]){to1.push_back(j);}
        for(int j : row[y[i] + 2]){to1.push_back(j);}

        if(mp[make_pair(x[i] - 1,y[i])]){to0.push_back(mp[make_pair(x[i] - 1,y[i])]);}
        if(mp[make_pair(x[i],y[i] - 1)]){to0.push_back(mp[make_pair(x[i],y[i] - 1)]);}
        if(mp[make_pair(x[i] + 1,y[i])]){to0.push_back(mp[make_pair(x[i] + 1,y[i])]);}
        if(mp[make_pair(x[i],y[i] + 1)]){to0.push_back(mp[make_pair(x[i],y[i] + 1)]);}
        // printf("dis[u = %d] = %d->\n",u,dis[u]);
        for(int v : to0){
            if(dis[v] > dis[u]){
                dis[v] = dis[u];
                que.push(node(dis[v],v));
            }
        }
        for(int v : to1){
            if(dis[v] > dis[u] + 1){
                dis[v] = dis[u] + 1;
                que.push(node(dis[v],v));
            }
        }
    }
    if(!mp[make_pair(n ,m)]){
        T = mp[make_pair(n, m)] = ++cnt;
        int pos = cnt;
        for(int j : line[n]){dis[T] = min(dis[T],dis[j] + 1);}
        for(int j : row[m]){dis[T] = min(dis[T],dis[j] + 1);}
        for(int j : line[n - 1]){dis[T] = min(dis[T],dis[j] + 1);}
        for(int j : row[m - 1]){dis[T] = min(dis[T],dis[j] + 1);}
    }
    printf("%d\n",dis[T] == 0x3f3f3f3f ? -1 : dis[T]);
    // for(int i = -2;i <= n;i++){
    //     for(int j = -2;j <= m;j++){
    //         if(mp[make_pair(i,j)]){
    //             printf("(%d,%d)id=%d dis=%d\n",i,j,mp[make_pair(i,j)],dis[mp[make_pair(i,j)]]);
    //         }
    //     }
    // }
    return 0;
}

CF821E

link

CF821E题意

你在 \((0,0)\)

\((x,y)\) 时,每次移动可以到达 \((x+1,y+1),(x+1,y),(x+1,y-1)\)

平面上有 \(n\) 条线段,平行于 \(x\) 轴,参数为\(a_i\) , \(b_i\) ,\(c_i\) ,表示在 \((a_i,c_i)\)\((b_i,c_i)\) 的一条线段,保证 \(b_i=a_{i+1}\)

要求你一直在线段的下方且在 \(x\) 轴上方,即 \(a_i \leq x \leq b_i\) 时, \(0 \leq y \leq c_i\)

\(:\) 到达 \((k,0)\) 的方案数,方案数对 \(10^9+7\) 取模。

\(n \leq 100 , k \leq 10^{18} , c_i \leq 15\)

CF821E题解

快速幂+矩阵优化DP。
直接想DP柿子,设 \(f_{i,j}\) 表示在点 \((i,j)\) 时的方案数,那么有

\[f_{i,j}=f_{i,j-1}+f_{i,j}+f_{i,j+1} \]

发现这玩应可以构造出来矩阵转移,形如:

\[\begin{bmatrix} 1&1&0&\dots&0&0&0\\ 1&1&1&\dots&0&0&0\\ 0&1&1&\dots&0&0&0\\ \vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots\\ 0&0&0&\dots&1&1&0\\ 0&0&0&\dots&1&1&1\\ 0&0&0&\dots&0&1&1\\ \end{bmatrix} \]

然后注意下大小即可。

CF821E代码

#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 = 21, mod = 1e9 + 7;
int siz;
struct Matrix{
    int d[maxn][maxn];
    Matrix(int x = 0,bool opt = 0){
        memset(d,0,sizeof(d));
        if(opt == 0){
            d[1][1] = 1;
            for(int i = 1;i < x;i++)
                d[i + 1][i] = d[i][i + 1] = d[i + 1][i + 1] = 1;
        }
        else for(int i = 1;i <= siz;i++)d[i][i] = 1;
    }
    void DEBUG(){
        for(int i = 1;i < maxn;i++){
            for(int j = 1;j < maxn;j++)
                printf("%lld ",d[i][j]);
            puts("");
        }
    }
    friend Matrix operator * (Matrix a,Matrix b){
        Matrix c = Matrix(0);c.d[1][1] = 0;
        for(int i = 1;i <= siz;i++)
            for(int k = 1;k <= siz;k++)
                for(int j = 1;j <= siz;j++)
                    c.d[i][j] = (c.d[i][j] + a.d[i][k] * b.d[k][j] % mod) % mod;
        return c;
    }
    friend Matrix operator ^ (Matrix x,int a){
        Matrix res = Matrix(0,1);
        while(a){
            if(a & 1)res = res * x;
            x = x * x;a >>= 1;
        }
        return res;
    }
}A;
int n, k;
signed main(){
    n = read();k = read();
    A = Matrix(0,0);
    for(int i = 1;i <= n;i++){
        int a = read(), b = read(), c = read();
        siz = c + 1;
        A = A * (Matrix(siz,0) ^ (min(b,k) - a));
        if(b >= k)break;
    }
    printf("%lld\n",A.d[1][1]);
    return 0;
}
posted @ 2023-12-08 07:30  Call_me_Eric  阅读(39)  评论(0)    收藏  举报
Live2D