AT_abc408
前言
这把手速失败了,共吃 4 发罚时,成为战犯。
A.Timeout
translation:
判断是否有 \(\forall 1\le i \le n,a_i-a_{i-1}\le S\),其中 \(a_0=0\)。
扫一遍即可。
这个屑翻译了 2min 题意,被嘲讽了(
#include<bits/stdc++.h>
using namespace std;
int S,T,a,b;
signed main(){
cin>>T>>S;
for(int i=1;i<=T;i++){
cin>>b;
if(b-a>S){
cout<<"No"<<endl;
return 0;
}
a=b;
}
cout<<"Yes"<<endl;
return 0;
}
B.Compression
translation:
将 \(A\) 排序并去重后输出。
食用 STL 库即可。STL 真是美味啊!
#include<bits/stdc++.h>
using namespace std;
int n,a[200005];
signed main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
n=unique(a+1,a+n+1)-a;
cout<<n-1<<endl;
for(int i=1;i<n;i++)cout<<a[i]<<" ";
return 0;
}
C.Not All Covered
translation:
给定若干个区间,求要存在一个点使得其不被任何区间覆盖,至少要删掉多少区间。
转换一下,题目问的就是做若干次区间 \(+1\) 然后查询全局最小值。
差分一下即可。
此人数组开小白吃一发罚时 😦
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000005];
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int l,r;cin>>l>>r;
a[l]++;a[r+1]--;
}
int mn=0x3f3f3f3f,A=0;
for(int i=1;i<=n;i++)A+=a[i],mn=min(mn,A);
cout<<mn<<endl;
return 0;
}
D.Flip to Gather
translation:
给定一个 \(01\) 串,将一个 \(0\) 变成 \(1\) 或将一个 \(1\) 变成 \(0\) 视为一次操作。求最少的操作数使得串内至多有一段连续的 \(1\)。
枚举连续 \(1\) 的结束点,计算后缀中 \(1\) 的个数。然后设 \(pre_{i,0/1}\) 为以 \(i\) 结尾且合法,最后一个字符为 \(0/1\) 的最小操作次数。那么有:
\(pre_{i,0}=pre_{i-1,0}+[s_i=1]\)
\(pre_{i,1}=\min(pre_{i-1,0},pre_{i-1,1})+[s_i=0]\)
最终答案为 \(\min_{1\le i\le n}pre_{i-1,1}+suf_i\)。
#include<bits/stdc++.h>
using namespace std;
int n;
char s[200005];
int pre[200005][2],suf[200005];
signed main(){
int T;cin>>T;
while(T--){
cin>>n;
scanf("%s",s+1);
pre[0][0]=pre[0][1]=0;
for(int i=1;i<=n;i++){
if(s[i]=='1'){
pre[i][0]=pre[i-1][0]+1;
pre[i][1]=min(pre[i-1][1],pre[i-1][0]);
}
else{
pre[i][0]=pre[i-1][0];
pre[i][1]=min(pre[i-1][1],pre[i-1][0])+1;
}
}
suf[n+1][0]=suf[n+1][1]=0;
int ans=min(pre[n][0],pre[n][1]);
for(int i=n;i>=1;i--){
suf[i]=suf[i+1]+(s[i]=='1');
ans=min(ans,suf[i]+pre[i-1][1]);
}
cout<<ans<<endl;
}
return 0;
}
E.Minimum OR Path
translation:
最短路,路径权值为路径上所有边边权按位或的结果。
从高位到低位判断最终结果能否在该位以下即可。
具体的,利用并查集求最小生成树,每次取出最大边边权的最高位 \(2^k\),去除所有边权 \(\ge 2^{k+1}\) 的边,剩下的所有边去掉 \(2^k\) 这一位,然后重复,直到没有剩下的边或者剩下的边权都为 \(0\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct edge{
int x,y,w;
bool operator < (const edge & B) const {
return w<B.w;
}
}e[200005];
int n,m;
int fa[200005];
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
signed main(){
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>e[i].x>>e[i].y>>e[i].w;
}
sort(e,e+m);
int ans=0;
for(int k=29;k>=0;k--){
for(int i=1;i<=n;i++)fa[i]=i;
int mx=0;
for(int i=0;i<m;i++){
int fx=find(e[i].x),fy=find(e[i].y);
mx=e[i].w;
if(fx==fy)continue;
fa[fx]=fy;
if(find(1)==find(n))break;
}
int S=log2(mx);
if(m==0||mx==0)break;
ans|=(1<<S);
m=lower_bound(e,e+m,edge{0,0,1<<(S+1)})-e;
for(int i=0;i<m;i++)if(e[i].w&(1<<S))e[i].w-=(1<<S);
sort(e,e+m);
}
cout<<ans<<endl;
return 0;
}
F.Athletic
translation:
给定一个排列 \(p\),规定位置 \(i\) 可以移动到任意满足 \(|i-j|\le R,p_j\le p_i+D\) 的位置 \(j\)。问从某个位置开始,至多能做多少次移动。
一个 dp。朴素的想法是按照 \(p_i\) 从大到小更新,每次取 \(dp_i=1+\max_{|i-j|\le R,p_j\le p_i+D}dp_j\),但是我们发现这是平方量级的。
考虑如何优化。我们发现这个更新过程本质上是一个区间最大值,于是考虑线段树。初始时每个位置都是 \(-1\),当我们更新到 \(p_i\) 时,将 \(p_{i}+D\)(如果存在)的值更新到线段树内,然后查询区间最大值并记录即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,mx[2000005];
void pushup(int x){
mx[x]=max(mx[x<<1],mx[x<<1|1]);
}
void build(int x,int l,int r){
if(l>r)return;
if(l==r){
mx[x]=-1;
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
void upd(int x,int l,int r,int ps,int k){
if(l==r){
mx[x]=k;
return;
}
int mid=(l+r)>>1;
if(ps<=mid)upd(x<<1,l,mid,ps,k);
else upd(x<<1|1,mid+1,r,ps,k);
pushup(x);
}
int query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R)return mx[x];
int mid=(l+r)>>1,res=-1;
if(L<=mid)res=max(res,query(x<<1,l,mid,L,R));
if(R>mid) res=max(res,query(x<<1|1,mid+1,r,L,R));
return res;
}
int p[500005],ip[500005],R,D,ans[500005];
signed main(){
cin>>n>>D>>R;
for(int i=1;i<=n;i++)cin>>p[i],ip[p[i]]=i;
build(1,1,n);
for(int i=n;i>=1;i--){
if(i+D<=n)upd(1,1,n,ip[i+D],ans[i+D]);
int l=max(1ll,ip[i]-R);
int r=min(n,ip[i]+R);
ans[i]=query(1,1,n,l,r)+1;
}
int res=0;
for(int i=1;i<=n;i++)res=max(res,ans[i]);
cout<<res<<endl;
return 0;
}
G.A/B < p/q < C/D
translation:
给定 \(A,B,C,D\),求最小的 \(q\) 使得存在 \(p\) 满足 \(\frac{A}{B}<\frac{p}{q}<\frac{C}{D}\)。
看到标题这怎么是典题?翻译一下还真是典中典啊!
P5179 Fraction 启动!
#include <bits/stdc++.h>
using namespace std;
#define int long long
void gcd(int a,int b,int&p,int&q,int c,int d) {
if(a<b&&c>d)p=q=1;
else {
gcd(d%c,c,q,p,b-(d/c)*a,a);
q+=(d/c)*p;
}
}
int a,b,c,d;
signed main(){
int T;cin>>T;
while(T--){
cin>>a>>b>>c>>d;
int p,q;gcd(a,b,p,q,c,d);
cout<<q<<'\n';
}
return 0;
}