AtCoder Beginner Contest 396(ABCDEF)
目录
A - Triple Four
翻译:
思路:
实现:
B - Card Pile
翻译:
思路:
实现:
C - Buy Balls
翻译:
思路:
实现:
D - Minimum XOR Path
翻译:
思路:
实现:
E - Min of Restricted Sum
翻译:
思路:
实现:
F - Rotated Inversions
翻译:
思路:
实现:
A - Triple Four
翻译:
你被给予一个长度为N的整数序列
。
确定A中是否存在一个地方连续出现三次或更多相同的元素。
更正式地,决定是否存在一个整数 i (1<=i<=N-2)如此
。
思路:
遍历A,判断
是否存在。
实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MX = 1e5+10;
void solve(){
int n;cin>>n;
vector<int> a(n);
for (int &i:a)cin>>i;
for (int i=0;i<n-2;i++){
if (a[i]==a[i+1]&&a[i+1]==a[i+2]){
cout<<"Yes"<<endl;
return;
}
}
cout<<"No"<<endl;
}
int main(){
// 关闭输入输出流同步
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
// 不使用科学计数法
// cout<<fixed;
// 中间填保留几位小数,不填默认
// cout.precision();
solve();
}
B - Card Pile
翻译:
有一叠 100 张卡片,每张都标有整数 0。
处理 Q 个查询。每个查询都属于以下类型之一:
- 类型 1:将一张标有整数 x 的卡片放在堆栈顶端。
- 类型 2:取出堆栈顶端的卡片,并输出写在该卡片上的整数。在此问题的限制条件下,牌堆中总是至少有一张牌。
思路:
这是一种数据结构先进先出(栈)。
实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MX = 1e5+10;
void solve(){
int n;cin>>n;
stack<int> nums;
for (int i=0;i<100;i++) nums.push(0);
int f,id;
while (n--){
cin>>f;
if (f==1){
cin>>id;
nums.push(id);
}else{
cout<<nums.top()<<"\n";
nums.pop();
}
}
}
int main(){
// 关闭输入输出流同步
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
// 不使用科学计数法
// cout<<fixed;
// 中间填保留几位小数,不填默认
// cout.precision();
solve();
}
C - Buy Balls
翻译:
有 N 个黑球和 M 个白球。
每个球有一个值。第 i 个黑球(1≤i≤N)的值为
,第 j 个白球(1≤j≤M)的值为
。.
选择 0 个或更多的球,使得所选黑球的个数至少等于所选白球的个数。在所有这些选择中,求所选球的最大可能数值之和。
思路:
题目要求黑球数量>=白球数量。贪心要选就选大的,要选就选正的。
实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MX = 1e5+10;
void solve(){
ll cnt_b,cnt_w;
cin>>cnt_b>>cnt_w;
vector<ll> W(cnt_w),B(cnt_b);
for (ll &i:B) cin>>i;
for (ll &i:W) cin>>i;
sort(W.begin(),W.end(),greater<int>());
sort(B.begin(),B.end(),greater<int>());
ll ans = 0;
ll j = 0,now_W=0,now_B = 0;
for (ll i=0;i<cnt_b;i++){
now_B += B[i];
for (;j<=i && W[j]>0 && j<cnt_w;j++) now_W+=W[j];
ans = max(ans,now_B+now_W);
}
cout<<ans<<endl;
}
int main(){
// 关闭输入输出流同步
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
// 不使用科学计数法
// cout<<fixed;
// 中间填保留几位小数,不填默认
// cout.precision();
solve();
}
D - Minimum XOR Path
翻译:
给你一个简单相连的无向图,图中有 N 个顶点(编号为 1 到 N)和 M 条边(编号为 1 到 M)。边 i 连接顶点
和
,其标签为
.
在所有简单路径(不多次通过同一顶点的路径)中,从顶点 1 到顶点 N 的所有简单路径(不多次经过同一顶点的路径)中,求路径上边的标签的最小XOR。
思路:
N的最大为10,那么暴力考虑事件复杂度为9!=362880<1e7。当算出来的时间复杂度<=1e7时一般这个算法就是可行的。所以直接深搜每条1到n的路径。(dfs)
实现:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MX = 11;
int n,m;
int ans = LLONG_MAX;
vector<vector<int>> graph(MX,vector<int>(MX,-1));
vector<int> vis(MX,0);
void dfs(int index,int val){
if (index==n){
ans = min(ans,val);
return;
}
for (int i=1;i<=n;i++){
if (graph[index][i]!=-1 && !vis[i]){
vis[i] = 1;
dfs(i,val^graph[index][i]);
vis[i] = 0;
}
}
}
void solve(){
cin>>n>>m;
for (int u,v,w,i=1;i<=m;i++){
cin>>u>>v>>w;
graph[u][v] = graph[v][u] = w;
}
vis[1] = 1;
dfs(1,0);
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
solve();
}
E - Min of Restricted Sum
翻译:
给你整数 N,M 和三个长度为 M 的整数序列:
。保证 X 和 Y 的所有元素都在 1 到 N 之间。
我们称长度为 N 的非负整数序列
为良好序列,当且仅当它满足以下条件时:
- 对于 1≤i≤M 的每一个整数 i,
和
的 XOR 为
.
判断一个好的序列
存在,并且如果存在,找出一个和最小的好序列。
。
思路:
异或有一个性质A^B=C,A^C=B,B^C=A。例如a->b异或得到x那么a^x=b,b^x=a。如何判断好序列存在呢?根据条件建立一个无向图,如果图中相连部分有不满足条件(如点b到点c得到10,而点a到点c得到的不是10)的输出-1。
那么如何得到和最小的好序列呢?想到异或是针对每个数的二进制数位的,也就是说每个数与它相连的数的相同数位变化是同时的,那么要让和最小就要让同一个联通块的每个二进制数位的0数量大于等于1的数量。(bfs,位运算)
实现:
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n,m;
cin>>n>>m;
// index val
vector<vector<pair<int,int>>> graph(n+1);
for (int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w;
graph[u].push_back({v,w});
graph[v].push_back({u,w});
}
// 记录答案
vector<int> ans(n+1,-1);
for (int i=1;i<=n;i++){
if (ans[i]!=-1) continue;
ans[i] = 0;
vector<int> groups={i};
// bfs:搜索与i有关的连通块
for (int j=0;j<groups.size();j++){
int val = ans[groups[j]];
for (auto& [to,now_val]:graph[groups[j]]){
if (ans[to]==-1){
groups.push_back(to);
ans[to] = now_val^val;
}else if (ans[to]!=(now_val^val)){
cout<<-1<<endl;
return;
}
}
}
// 遍历位数
for (int j=0;j<=30;j++){
int cnt1 = 0;
// 第j位中有多少1
for (auto& num:groups){
if (ans[num]&(1<<j)){
cnt1++;
}
}
// j位中1的数量大于0的数量
if (2*cnt1>groups.size()){
// 将j位中1,0互换
for (auto &num:groups){
ans[num]^=(1<<j);
}
}
}
}
for (int i=1;i<=n;i++){
if (i!=1) cout<<" ";
cout<<ans[i];
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
solve();
}
F - Rotated Inversions
翻译:
给你整数 N,M 和长度为 N 的非负整数序列
。
对于 k=0,1,...,M-1,求解以下问题:
定义一个整数序列 B
,使得
是
除以 M 的余数。找到B的逆序对数量。
思路:
对于k变为k+1时,哪些值是对逆序对数量有影响的?k+1时会到m变为0的值。那么在k=0的基础上,当k=i时要找值为m-i的下标的数组change。change中第 i 个值,在这 i 个前面的值除去change中的都为逆序对 : chage[i] - 1 -i 。i 后面的值除去change中的都不为逆序对:n-change[i]-(change.size()-(i+1))。
那么原数组怎么求逆序对呢?使用权值线段树求逆序对,对于每个插入的数求出大于该数的数量。(zkw线段树)
实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MX = 2e5;
// 权值线段树
ll tree[MX<<2];
ll tree_m;
void add(ll position){
for (position+=tree_m;position>0;position>>=1){
tree[position]++;
}
}
ll find(ll l,ll r){
ll res = 0;
for (l+=tree_m,r+=tree_m;l<=r;l>>=1,r>>=1){
if (l%2==1) res+=tree[l++];
if (r%2==0) res+=tree[r--];
}
return res;
}
void solve(){
ll n,m;
cin>>n>>m;
tree_m = 1;
while (tree_m<m) tree_m<<=1;
ll cnts = 0;
vector<ll> a(n+1);
vector<vector<ll>> change(m);
for (ll i=1;i<=n;i++) {
cin>>a[i];
change[a[i]].push_back(i);
// 求version number
add(a[i]);
cnts+=find(a[i]+1,m);
}
cout<<cnts<<"\n";
// 当k为i是要关心m-i
for (ll i=m-1;i>=1;i--){
ll now_cnt = 0;
for (ll j=0;j<change[i].size();j++){
now_cnt += change[i][j]-1-j;
now_cnt -= n-change[i][j]-(change[i].size()-(j+1));
}
cnts+=now_cnt;
cout<<cnts<<"\n";
}
}
signed main(){
// 关闭输入输出流同步
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
// 不使用科学计数法
// cout<<fixed;
// 中间填保留几位小数,不填默认
// cout.precision();
solve();
}
有建议可以评论,我会积极改进qwq。