Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)简训
导语
日常训练,终于多做出来一道题
涉及的知识点
思维,前缀和,树状数组/线段树
链接:Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)
题目
A Game
题目大意:略
思路:统计起点连续1最远能到哪,统计终点连续1最前能到哪,然后获得两个位置的距离即可,如果都是1直接输出0
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
using namespace std;
int n,m,cnt,t,a[121];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
cin >>n;
int r=n,l=1;
for(int i=1; i<=n; i++)
cin >>a[i];
if(n==2) {//特判
cout <<0<<endl;
continue;
}
while(a[r-1])r--;//终点连续1
while(a[l+1])l++;//起点连续1
cout <<(r-l>0?r-l:0)<<endl;
}
return 0;
}
B Game of Ball Passing
题目大意:n个人彼此传球,给出最后每个人传球的次数,判断至少几个球能满足最后的传球次数结果
思路:如果次数最大值能够大于其余所有项之和,代表需要额外的球来消耗最大值剩下的值,否则只需要一个球
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
using namespace std;
int n,m,cnt,t,a[maxn];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
cin >>n;
int sum=0,mx=-1,ans=0;
for(int i=1; i<=n; i++) {
cin >>a[i],mx=max(mx,a[i]),sum+=a[i];
//获得最大值和统计和
if(a[i])ans++;
}
if(ans==0) {//特判全是0
cout <<0<<endl;
continue;
}
sum-=mx;
if(mx<=sum+1)cout <<1<<endl;
else cout <<mx-sum<<endl;
}
return 0;
}
C Weird Sum
题目大意:给出n×m的矩阵,每个位置填的数字是1到n和1到m,只能填一个,求出每对相同数字的在矩阵上的曼哈顿距离和
思路:对每个数字可以分解成行坐标和列坐标,分别保存,那么分开来处理即可,比如遍历行坐标,增加的距离为当前的行坐标分别减去前面已经出现的行坐标的差值的和,可以使用前缀和来简化过程,对列坐标也是如此,但是需要先排序,最后统计结果即可
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
using namespace std;
int n,m,t;
vector<int>x[maxn],y[maxn];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>n>>m;
int sum=0,mx=0;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++) {
int v;
cin >>v;
mx=max(mx,v);
x[v].push_back(i);//记录横坐标
y[v].push_back(j);//记录纵坐标
}
for(int i=1; i<=mx; i++) {
int len=x[i].size(),prex=0,prey=0;//初始化长度和前缀和
if(len==1||len==0)continue;//特判
sort(y[i].begin(),y[i].end());//只需要对列坐标排序
for(int j=0; j<len; j++) {
if(j==0) {//第一个跳过
prex+=x[i][j];
continue;
}
sum+=(j)*x[i][j]-prex;//记录这个值与所有已经出现过的值的差值
prex+=x[i][j];//统计前缀和
}
for(int j=0; j<len; j++) {//同上
if(j==0) {
prey+=y[i][j];
continue;
}
sum+=(j)*y[i][j]-prey;
prey+=y[i][j];
}
}
cout <<sum<<endl;
return 0;
}
D Integral Array
题目大意:略
思路:假设 x , y ∈ a , r ∉ a x,y\in a,r\notin a x,y∈a,r∈/a,如果 ⌊ x / y ⌋ = r ⌊x/y⌋=r ⌊x/y⌋=r,那么 y r ≤ x < y ( r + 1 ) yr\le x\lt y(r+1) yr≤x<y(r+1),将思路反过来,如果枚举所有不属于a的r,和所有属于a的y,如果能找到一个x属于给定的区间,那么必然可以判断序列不符合题设条件,将r从1到c枚举,y从1到yr>c位置,利用前缀和判断是否存在这样的一个x,时间复杂度为 O ( C log C ) O(C\log C) O(ClogC),证明可以查看参考文献
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
const int maxn=2e6+5;
using namespace std;
int n,c,t,pre[maxn];
bool a[maxn];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
cin >>n>>c;
for(int i=1; i<=c; i++)//初始化
a[i]=pre[i]=0;
for(int i=1; i<=n; i++) {
int x;
cin >>x;
a[x]=1;//标记存在
}
bool flag=0;
for(int i=1; i<=c; i++)pre[i]=pre[i-1]+a[i];
//利用前缀和判断区间内是否存在值
for(int r=1; r<=c; r++) {
if(flag)break;//找到了
if(a[r])continue;//如果在序列中出现,那么必然会作为y,跳过
for(int y=1; y*r<=c; y++) {//不超过边界
if(!a[y])continue;//如果这个数不存在
if(pre[min(y*(r+1)-1,c)]-pre[r*y-1]) {
//判断区间内是否存在值
flag=1;
break;
}
}
}
flag?cout <<"NO\n":cout <<"YES\n";
}
return 0;
}