codeforces round 922 (div. 2) A-D
前言:
打的VP,真实实力应该是够我做到前三题。按照同层次的比下去,我们学校估计明年就有人AK IOI了\se\se。昨天水课的时候感觉自己日益颓废ing。
Codeforces Round 922 (Div. 2) VP记录
A. Brick Wall
就是一张 \(n*m\) 的地图,问你最多可以铺几块 \(1*k\) \((k \ge 2)\) 的地板,并且方向固定不能旋转,那么贪心的想肯定是取 \(k=2\),那么答案就是 \(n*(m/2)\)。
代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
#define int long long
using namespace std;
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x>y?y:x;}
const int M=1;
int T,n,m;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--)
{
cin>>n>>m;
cout<<n*(m/2)<<"\n";
}
return 0;
}
B. Minimize Inversions
题意是你需要让两个数组的逆序对总数最少,你可以交换无数次,但是每次交换的时候 \(a\) 数组交换了 \(b\) 数组也要交换,做的时候并没有想到严谨的做法,懵逼状态下猜出大概的正解了。
其实贪心的想,就是你只在 $ a_i \gt a_j $ 并且 $ b_i \gt b_j $时交换 \(i\),\(j\) 两个下标就可以了。处理也比较简单。
代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
#define int long long
using namespace std;
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x>y?y:x;}
const int M=2e5+5;
int T,n;
int a[M],b[M],c[M];
inline bool cmp(int x,int y)
{
return a[x]<a[y];
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) c[i]=i;
sort(c+1,c+n+1,cmp);
for(int i=1;i<=n;i++) cout<<a[c[i]]<<" ";
cout<<"\n";
for(int i=1;i<=n;i++) cout<<b[c[i]]<<" ";
cout<<"\n";
}
return 0;
}
C. XOR-distance
我觉得我的代码应该是实现起来最简单的一个(VP的时候调了半天),给定 \(a\) , \(b\) , \(r\) ,求 $|({a \oplus x}) - ({b \oplus x})| $,\(0 \leq x \leq r\) 。那么我们可以将 \(a\),\(b\) 都化作二进制,最多也就60位(这里你可能需要用1ll来进行位运算)。
对于两个数的每一位,如果这一位上两个数相同,那么由于最后两个数是要相减的,最后会变为0,那么可以直接跳过。着重要讨论的是这一位不同。首先设 \(a\) 为较大数,由于这一位上两个数不同,最后异或上 \(r\) 后两数这一位也还是不同。那么贪心的想,从高位向低位,扫到第一位不同的就跳过,相当于是让给了 \(a\),之后再有不同的情况,如果这一位上 \(a\) 是1,可以减你就减(毕竟还有 \(r\) 的限制)。
代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<vector>
#define int long long
using namespace std;
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x>y?y:x;}
const int M=1e5+5;
int T,a,b,r;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--)
{
cin>>a>>b>>r;
if(a<b) swap(a,b);
int ans=0,flag=0;
for(int i=(1ll<<60);i;i>>=1)
{
int opt1=i&a,opt2=i&b;
if(opt1==opt2) continue;
if(flag==0)
{
flag=1;continue;//让出一位
}
if(opt1&&i<=r) a-=i,b+=i,r-=i;//此时 a这一位是1,b这一位是0
}
cout<<a-b<<"\n";
}
return 0;
}
D. Blocking Elements
我自觉没有实力可以在oi赛制下把这道题切了,我谔谔。
很显然可以想出二分答案,一开始我是遍历整个数组,如果大于了判定值,那么就分段(假的不能再假了),这题显然不是贪心。那么我们就需要设计以第 \(i\) 个数作为一段结尾的状态转移,每次枚举前面的某个点作为上一段结尾,然后使用队列&前缀和维护。
哦,还要开long long。
代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
#define int long long
using namespace std;
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x>y?y:x;}
const int M=1e5+5;
int T,n,sum=0,maxx=0;
int a[M],s[M],q[M],f[M];
inline int check(int x)
{
int l=1,r=1;
q[r++]=0,f[1]=a[1];//记录每一段
q[r]=1;
for(int i=2;i<=n;i++)
{
while(s[i-1]-s[q[l]]>x) l++;
f[i]=a[i]+f[q[l]];
while(r>=l&&f[q[r]]>=f[i]) r--;
q[++r]=i;//又成一段结尾
}
while(s[n]-s[q[l]]>x) l++;
if(f[q[l]]<=x) return 1;
else return 0;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--)
{
cin>>n;sum=maxx=0;
for(int i=1;i<=n;i++)
{
cin>>a[i],sum+=a[i],maxx=max(maxx,a[i]);
s[i]=s[i-1]+a[i];
}
int l=maxx,r=sum;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<"\n";
}
return 0;
}