CF821题解
CF821
CF821A
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
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\) 是整数的时候,\((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
CF821C题意
Okabe和Daru正在向一个栈中加入和删除盒子。盒子编号从 \(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
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
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)\) 时的方案数,那么有
发现这玩应可以构造出来矩阵转移,形如:
然后注意下大小即可。
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;
}

浙公网安备 33010602011771号