AtCoder Beginner Contest 378 题解
比赛链接
A - Pairing 贪心
#include<bits/stdc++.h>using namespace std;using i64=long long;void Showball(){vector<int> a(5);for(int i=0;i<4;i++){int x;cin>>x;a[x]++;}int cnt=0;for(int i=1;i<=4;i++) cnt+=a[i]/2;cout<<cnt<<"\n";
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int t=1;//cin>>t;while(t--){Showball();}return 0;
}
B - Garbage Collection
#include<bits/stdc++.h>using namespace std;using i64=long long;void Showball(){int n;cin>>n;vector<int> q(n),r(n);for(int i=0;i<n;i++){cin>>q[i]>>r[i];}int m;cin>>m;while(m--){int x,d;cin>>x>>d;x--;cout<<d+(q[x]+r[x]-d%q[x])%q[x]<<"\n";}
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int t=1;//cin>>t;while(t--){Showball();}return 0;
}
C - Repeating
使用 map
开个桶记录一下每个数上次出现的位置即可。
#include<bits/stdc++.h>using namespace std;using i64=long long;void Showball(){int n;cin>>n;vector<int> a(n);for(int i=0;i<n;i++){cin>>a[i];}map<int,int> mp;for(int i=0;i<n;i++){if(!mp[a[i]]) cout<<"-1 ";else cout<<mp[a[i]]<<" ";mp[a[i]]=i+1;}
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int t=1;//cin>>t;while(t--){Showball();}return 0;
}
D - Count Simple Paths 深搜
数据范围很小,直接枚举每个点,然后从每个点开始深搜即可。标准的深搜,写法比较套路。注意 st
数组的更新。
#include<bits/stdc++.h>using namespace std;using i64=long long;void Showball(){int h,w,k;cin>>h>>w>>k;vector<string> a(h);for(int i=0;i<h;i++){cin>>a[i];}vector<vector<int>> st(h,vector<int>(w));auto check=[&](int x,int y){return (0<=x&&x<h&&0<=y&&y<w&&a[x][y]=='.'&&!st[x][y]);};const int dx[]={1,-1,0,0};const int dy[]={0,0,1,-1};int ans=0;function<void(int,int,int)> dfs=[&](int x,int y,int cnt){if(cnt>=k){ans++;return;}for(int i=0;i<4;i++){int xx=x+dx[i],yy=y+dy[i];if(check(xx,yy)){st[xx][yy]=1;dfs(xx,yy,cnt+1);st[xx][yy]=0;}}};for(int i=0;i<h;i++){for(int j=0;j<w;j++){if(a[i][j]=='.') {st[i][j]=1;dfs(i,j,0);st[i][j]=0;}}}cout<<ans<<"\n";
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int t=1;//cin>>t;while(t--){Showball();}return 0;
}
E - Mod Sigma Problem 思维
如果两层都要取模,那就非常简单,只有一层,我们先不管取模尝试化简这个式子。
考虑使用前缀和,原式 = $\sum_{1\le l,r \le N} ({S_r-S_{l-1}}) $ 。
继续化简: \(\sum_{1\le i \le N} \sum_{1\le j \le i} ({S_i-S_{j-1}})\) = \(\sum_{1\le i \le N} \sum_{1\le j \le i-1} ({S_i-S_{j}})\)
把 \(S_i\) 拿出来,即:\(\sum_{1\le i \le N} ({i*S_i-\sum_{1\le j\le i-1}S_{j}})\) 。
式子被我们化成了差的形式,因此,如果 \(S_i < S_j\) 那么结果就会变成负数,因此我们就需要加上一个 \(m\) 。
考虑如何快速统计需要增加的 \(m\) 的数量。发现问题转化为求在 \(s_i\) 之前有多少个数比它大。
其实就是经典的逆序对问题,使用归并排序或者树状数组即可解决。
这里采用树状数组,注意,这里的数组值要对 \(m\) 取模,值可能为 \(0\),因此,使用权值树状数组时,可能会 TLE
。
所以,我们只需要偏移一位下标即可。
#include<bits/stdc++.h>using namespace std;using i64=long long;void Showball(){int n,m;cin>>n>>m;vector<i64> a(n+1),s(n+1);for(int i=1;i<=n;i++){cin>>a[i];s[i]=(s[i-1]+a[i])%m;}vector<i64> tr(m+2);auto add=[&](int x){for(;x<=m;x+=x&-x) tr[x]++;};auto getsum=[&](int x){i64 ret=0;for(;x;x-=x&-x) ret+=tr[x];return ret;};i64 tot=0,res=0;for(int i=1;i<=n;i++){res+=i*s[i]-tot;tot+=s[i];res+=1LL*m*(getsum(m)-getsum(s[i]+1));add(s[i]+1);}cout<<res<<"\n";
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int t=1;//cin>>t;while(t--){Showball();}return 0;
}
F - Add One Edge 2 图论
要构成满足条件的环,我们只需要首尾点的度数为 \(2\) , 中间点的度数为 \(3\) 即可。发现度数为 \(3\) 的点组成的联通块周围的度数为 \(2\) 的点两两都是可达的。
那么我们只需要维护出每个点周围有多少个度数为 \(2\) 的点,然后乘法原理即可。最后 \(BFS\) 统计一下答案即可。
#include<bits/stdc++.h>using namespace std;using i64=long long;void Showball(){int n;cin>>n;vector<vector<int>> e(n);vector<int> d(n);for(int i=1;i<n;i++){int u,v;cin>>u>>v;u--,v--;e[u].push_back(v);e[v].push_back(u);d[u]++;d[v]++;} vector<int> w(n);function<void(int,int)> dfs=[&](int u,int fa){for(auto v:e[u]){w[u]+=(d[v]==2);if(v==fa) continue;dfs(v,u);}};auto bfs=[&](int st){int ret=0;queue<int> q;q.push(st);while(!q.empty()){int u=q.front();q.pop();ret+=w[u];d[u]=0;for(auto v:e[u]){if(d[v]!=3) continue;q.push(v);}}return ret;};dfs(0,-1);i64 ans=0;for(int i=0;i<n;i++){if(d[i]==3){int t=bfs(i);ans+=1LL*t*(t-1)/2;}}cout<<ans<<"\n";
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int t=1;//cin>>t;while(t--){Showball();}return 0;
}