【题解】Atcoder Beginner Contest 442(ABC442) A~F
A - Count .
直接模拟即可。
#include<bits/stdc++.h>
using namespace std;
string s;
int ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>s;
for(int i=0;i<s.size();i++){
if(s[i]=='i'||s[i]=='j') ans++;
}
cout<<ans;
return 0;
}
B - Music Player
直接模拟即可。
#include<bits/stdc++.h>
using namespace std;
int q,vol;
bool t;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>q;
for(int i=1;i<=q;i++){
int a;
cin>>a;
if(a==1) vol++;
else if(a==2) vol=max(0,vol-1);
else t^=1;
if(t&&vol>=3) cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
C - Peer Review
统计与每个人有冲突的人的个数,在剩余人里任选三个,即 \(C_m^3\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,m;
int cnt[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
cnt[u]++,cnt[v]++;
}
for(int i=1;i<=n;i++){
int t=n-cnt[i]-1;
if(t<=2) cout<<"0 ";
else cout<<t*(t-1)*(t-2)/6<<' ';
}
return 0;
}
D - Swap and Range Sum
注意到交换 \(A_x\) 和 \(A_{x+1}\),只会改变 \(x\) 处的前缀和。只需要修改 \(S_x\) 为 \(S_{x-1}+S_{x+1}-S_x\),用前缀和查询。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,q;
int a[N],sum[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=q;i++){
int op;
cin>>op;
if(op==1){
int x;
cin>>x;
sum[x]=sum[x-1]+sum[x+1]-sum[x];
}else{
int l,r;
cin>>l>>r;
cout<<sum[r]-sum[l-1]<<'\n';
}
}
return 0;
}
E - Laser Takahashi
一次查询相当于询问介于 \(A,B\) 之间的射线个数,前缀和维护每条射线上的点数量。但如何对射线排序是一个棘手的问题。我们采用极角排序,按 \(\theta\) 顺时针排序。
若直接计算斜率,则精度可能不够。我们把每个点的 \(x,y\) 坐标约分,这样同一方向上的点都共用一个 \(x,y\)。然后引入平面向量叉积的概念:设平面直角坐标系中有两个向量 \(\vec{a}=(x_1,y_1),\vec{b}=(x_2,y_2)\),则平面向量叉积 \(\vec{a}\times\vec{b}=x_1y_2-x_2y_1\)。若 \(\vec{a}\times\vec{b}>0\),则 \(\vec{b}\) 在 \(\vec{a}\) 的逆时针方向;若 \(\vec{a}\times\vec{b}<0\),则 \(\vec{b}\) 在 \(\vec{a}\) 的顺时针方向;若 \(\vec{a}\times\vec{b}=0\),则 \(\vec{a}\) 与 \(\vec{b}\) 共线。共线包含同向和反向,所以我们需先按照象限排序,若 \(\vec{a}\) 在一二象限,\(\vec{b}\) 在三四象限,则直接让 \(\vec{a}\) 排在 \(\vec{b}\) 后面。
计算时,若 \(A\) 在 \(B\) 的顺时针方向,则直接前缀和相减。否则,是从 \(A\) 转一圈到 \(B\),也就是总和减 \(A,B\) 间的和。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,q;
int cnt[N],id[N],sum[N];
vector<pair<int,int>> dir;
map<pair<int,int>,vector<int>> mp;
int gcd(int a,int b){
if(!b) return a;
else return gcd(b,a%b);
}
bool cmp(pair<int,int> a,pair<int,int> b){
bool p=(a.second>0||(!a.second&&a.first>0));
bool q=(b.second>0||(!b.second&&b.first>0));
if(p!=q) return p>q;
else return a.first*b.second>b.first*a.second;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
int g=gcd(abs(x),abs(y));
if(g) x/=g,y/=g;
if(!mp[{x,y}].size()) dir.push_back({x,y});
mp[{x,y}].push_back(i);
}
sort(dir.begin(),dir.end(),cmp);
for(int i=0;i<dir.size();i++){
cnt[i+1]=mp[dir[i]].size();
for(int k=0;k<mp[dir[i]].size();k++) id[mp[dir[i]][k]]=i+1;
}
for(int i=1;i<=dir.size();i++) sum[i]=sum[i-1]+cnt[i];
for(int i=1;i<=q;i++){
int a,b;
cin>>a>>b;
int x=id[a],y=id[b];
if(x==y) cout<<cnt[x]<<'\n';
else if(x>y) cout<<sum[x]-sum[y-1]<<'\n';
else cout<<sum[dir.size()]+sum[x]-sum[y-1]<<'\n';
}
return 0;
}
F - Diagonal Separation 2
第一个条件,即每一行存在一个分界点 \(k\),使得 \(k\) 及之前是白,后是黑。
第二个条件,即从上往下每一行的 \(k\) 单调不增,才能保证每一列上白下黑。
考虑前缀和,对每一行计算前缀白格数,若该行选择 \(k\) 为分界点,则需将修改 \(k\) 及前黑,\(k\) 后白,即 \(k-s_{i,k}+s_{i,n}-s_{i,k}\)。
定义 \(f_{i,j}\) 为第 \(i\) 行选择 \(j\) 为分界点所需最小花费,则 \(f_{i,j}=\min{f_{i-1,k}+(j-s_{i,j}+s_{i,n}-s_{i,j})},j\le k\le n\)。
暴力求解为 \(O(n^3)\),考虑优化。
我们每次只需要上一行 \(\ge j\) 的最小值,因此维护 \(d_j\) 为上一行的后缀最小值,不需要枚举每一个 \(k\)。时间复杂度 \(O(n^2)\)。
计算出第一行的 \(f\),向下转移。最终答案为第 \(n\) 行的最小值。
#include<bits/stdc++.h>
using namespace std;
const int N=5010;
const int INF=1e9;
int n,ans=INF;
int sum[N][N],f[N][N],d[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
char c;
cin>>c;
sum[i][j]=sum[i][j-1]+(c=='.');
}
}
for(int i=0;i<=n;i++) f[1][i]=i+sum[1][n]-2*sum[1][i];
for(int i=2;i<=n;i++){
d[n]=f[i-1][n];
for(int j=n-1;j>=0;j--) d[j]=min(d[j+1],f[i-1][j]);
for(int j=0;j<=n;j++) f[i][j]=d[j]+j+sum[i][n]-2*sum[i][j];
}
for(int i=0;i<=n;i++) ans=min(ans,f[n][i]);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号