AtCoder Beginner Contest 186
B - Blocks on Grid(模拟)
参考代码
点此展开
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int g[N][N];
int main()
{
int h,w;
cin>>h>>w;
int mmin=(int)110;
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
cin>>g[i][j];
mmin=min(mmin,g[i][j]);//找到最小值
}
}
int ans=0;
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
ans+=g[i][j]-mmin;//全部都减少到最小值
}
}
cout<<ans;
return 0;
}
C - Unlucky 7(模拟)
参考代码
点此展开
#include<bits/stdc++.h>
using namespace std;
//计算二进制
bool b(int num){
while(num){
if(num%10==7) return false;
num/=10;
}
return true;
}
//计算八进制
bool o(int num){
while(num){
if((num&7)==7) return false;
num>>=3;
}
return true;
}
int main()
{
int n;
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
if(b(i)&&o(i)) ans++;
}
cout<<ans<<endl;
return 0;
}
D - Sum of difference(数学)
题意
给定\(N\)个整数\(A_1,\dots,A_N\),计算\(\sum\limits_{i=1}^{N-1}\sum\limits_{j=i+1}^{N}\mid A_i-A_j\mid\)。
思路
增加了绝对值符号后,由于我们无法直接确定\(A_i,A_j\)大小关系,因此我们就无法直接进行化简。
为了解决这个问题,由于数组中的每一对都会出现,因此我们可以对数组进行排序,比如说从小到大,这样对于\(1\le i<j\le N\),我们就知道\(A_j>A_i\),因此上述的数学公式就可以转化成\(\sum\limits_{i=1}^{N-1}\sum\limits_{j=i+1}^{N} A_j-A_i\)。从而我们就可以利用前缀和计算出所需要的结果。
参考代码
点此展开
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
int n;
cin>>n;
vector<LL> a(n);
LL tot=0;
for(int i=0; i<n; i++){
cin>>a[i];
tot+=a[i];
}
sort(a.begin(), a.end());
LL res=0;
for(int i=0;i<n;i++){
res+=tot-(n-i)*a[i];
tot-=a[i];
}
cout<<res<<endl;
return 0;
}
E - Throne(数学)
题意
给定一个由\(n\)个结点组成的环,你将顺时针移动,现在你位于结点\(s\),每次移动\(k\)步,问经过多少次移动后你可以移动到结点\(0\),如果不能到达,输出\(-1\)。
思路
假设最少需要移动\(m\)次,用数学公式表达即\(s+m\cdot k\equiv0(\bmod n)\)。同时我们可以得到\(m\cdot k\equiv n-s(\bmod n)\),这是一个线性同余方程。套用线性同余方程求最小解模板即可。
参考代码
点此展开
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//拓展欧几里得算法
LL extgcd(LL a,LL b,LL &x,LL &y)
{
LL d=a;
if(b==0)
{
x=1,y=0;
}
else
{
d=extgcd(b,a%b,y,x);
y-=(a/b)*x;
}
return d;
}
//使用拓展欧几里得算法求逆元
LL get_inverse(LL a,LL m)
{
LL x,y;
extgcd(a,m,x,y);
return (x%m+m)%m;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
LL t=0;
cin>>t;
while(t--)
{
LL n,s,k;
cin>>n>>s>>k;
LL d=__gcd(n,k);
if((n-s)%d)//如果不能满足条件
{
cout<<-1<<endl;
}
else
{
n/=d,k/=d;
LL x=get_inverse(k,n);
cout<<x*(n-s/d)%n<<endl;
}
}
return 0;
}
F - Rook on Grid
题意
给定一个\(h\times w\)的棋盘,其中有\(m\)个障碍,你从\((0,0)\)点出发,每次向下或者向右移动,不限距离,遇到障碍后不能继续移动。你只能移动两次,求出移动两次,你可以访问到的棋盘上格子的个数。
思路
参考样例\(2\),我们会得到如下的一个棋盘(\(1\)代表障碍)
其中我们发现浅粉色的格子都是重复计算过的,所以我们可以考虑先将每种走法能够访问到的格子数量全部计算出来,之后再减去多计算的部分。
为了计算出多余的部分,我们考虑固定计算一种模式走法下重复计算过的格子数。比如我们固定计算先走右再走下的走法,那么对于某一行来说,重复计算的格子数目就是先前行中没有障碍的列的个数,而这个我们可以通过线段树或者树状数组来实现。
参考代码
点此展开
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+10;
int h,w,m;
int x[N],y[N];
struct BIT{
vector<LL> node;
int size;
BIT(int n){
size=n;
node.resize(size,0);
}
void add(int a,int x){
for(int i=a;i<size;i|=i+1) node[i]+=x;
}
LL sum(int a){
LL res=0;
for(int i=a-1;i>=0;i=(i&(i+1))-1){
res+=node[i];
}
return res;
}
};
int main()
{
cin>>h>>w>>m;
for(int i=0;i<m;i++)
{
cin>>x[i]>>y[i];
x[i]--,y[i]--;//从(0,0)开始
}
//计算从(0,0)能走到的最右和最下
int clsh=h,clsw=w;
for(int i=0;i<m;i++)
{
if(x[i]==0)
clsw=min(clsw,y[i]);
if(y[i]==0)
clsh=min(clsh,x[i]);
}
//接下来计算一下能走到的行和列遇到的最近障碍位置
vector<int> pih(clsh,w),piw(clsw,h);
for(int i=0;i<m;i++)
{
if(x[i]<clsh)
pih[x[i]]=min(pih[x[i]],y[i]);
if(y[i]<clsw)
piw[y[i]]=min(piw[y[i]],x[i]);
}
//结果
LL res=0;
//先统计总的
for(int i=0;i<clsh;i++) res+=pih[i];
for(int i=0;i<clsw;i++) res+=piw[i];
//统计一下第i行需要删除的位置
vector<vector<int>> dels(h+1);
for(int i=0;i<clsw;i++)
dels[piw[i]].push_back(i);
BIT bt(w);
for(int i=0;i<clsw;i++) bt.add(i,1);
for(int i=0;i<clsh;i++)
{
for(int d:dels[i])
bt.add(d,-1);
res-=bt.sum(pih[i]);
}
cout<<res<<endl;
return 0;
}