Loading

【题解】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;
}
posted @ 2026-02-01 19:33  Seqfrel  阅读(50)  评论(0)    收藏  举报