AtCoder Beginner Contest 356
ABC356 A - Subsegment Reverse
代码(签到题)
#include <cstdio>
#include <cctype>
#include <bitset>
#include <algorithm>
using namespace std;
int n,a[111],l,r;
int main(){
scanf("%d%d%d",&n,&l,&r);
for (int i=1;i<=n;++i) a[i]=i;
reverse(a+l,a+1+r);
for (int i=1;i<=n;++i) printf("%d ",a[i]);
return 0;
}
ABC356 B - Nutrients
代码(签到题)
#include <cstdio>
#include <cctype>
#include <bitset>
#include <algorithm>
using namespace std;
int n,a[111],m;
int main(){
scanf("%d%d",&m,&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
for (int i=1;i<=m;++i)
for (int j=1,x;j<=n;++j) scanf("%d",&x),a[j]-=x;
for (int i=1;i<=n;++i)
if (a[i]>0) return !printf("No");
return !printf("Yes");
}
ABC356 C - Keys
分析
直接枚举真的钥匙的 \(2^n\) 种状态就可以了
代码
#include <cstdio>
#include <cctype>
#include <bitset>
#include <algorithm>
using namespace std;
int n,xo[40011],a[111],b[111],m,k,ans;
int main(){
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<(1<<n);++i) xo[i]=xo[i&(i-1)]+1;
for (int i=1;i<=m;++i){
int j,x; char ch;
for (scanf("%d",&j);j;--j) scanf("%d",&x),a[i]|=1<<(x-1);
ch=getchar();
while (ch!='o'&&ch!='x') ch=getchar();
b[i]=(ch=='o');
}
for (int S=0;S<(1<<n);++S){
int flag=1;
for (int i=1;i<=m;++i)
if ((xo[a[i]&S]>=k)^b[i]) flag=0;
ans+=flag;
}
return !printf("%d",ans);
}
ABC356 D - Masked Popcount
分析
对 \(m\) 的每一位 \(1\) 进行拆位求解,如果 \(n\) 的这一位也为 \(1\),
那么 \(n\) 的最后几位都可以填 \(\leq n\) 的数,高位保持不变。
否则就让 \(n\) 的某个为 \(1\) 的高位填 \(0\),那么这一位填 \(1\),其它低位想填什么都可以。
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
long long n,m,ans;
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int main(){
scanf("%lld%lld",&n,&m);
for (int i=0;i<60;++i)
if ((m>>i)&1){
if ((n>>i)&1) ans=(ans+n-((n>>i)<<i)+1)%998244353;
for (int j=59;j>i;--j)
if ((n>>j)&1) ans=(ans+(1ll<<(j-1)))%998244353;
}
return !printf("%lld",ans);
}
ABC356 E - Max/Min
分析
不妨枚举较小值,如果较大值与较小值相等那就是组合数,否则可以通过枚举倍数的方式,
因为此时在某段区间里商都是一样的,此时对次数求个前缀和预处理即可。
代码
#include <cstdio>
#include <cctype>
#include <bitset>
#include <algorithm>
using namespace std;
const int N=1000011;
int n,mx,c[N],s[N]; long long ans;
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i){
int x; scanf("%d",&x);
mx=max(mx,x),++c[x];
}
for (int i=1;i<=mx;++i) s[i]=s[i-1]+c[i];
for (int i=1;i<=mx;++i) if (c[i])
for (int j=i;j<=mx;j+=i){
if (i==j) ans+=c[i]*(c[i]-1ll)/2;
ans+=1ll*c[i]*(j/i)*(s[min(mx,j+i-1)]-s[j-(i!=j)]);
}
return !printf("%lld",ans);
}
ABC356 F - Distance Component Size Query
分析
维护两个平衡树,一个平衡树装入集合中的数,另一个装入连通块的左右端点,
再用动态开点的权值线段树维护,查询时找到连通块的左右端点查询个数。
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <set>
using namespace std;
typedef long long lll;
const lll INF=2333333333333333333ll;
const int N=200011; lll d,x,pre,nxt;
set<lll>K,F; set<lll>::iterator it;
int Q,root,tot,w[N<<6],ls[N<<6],rs[N<<6];
lll iut(){
lll ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
void update(int &rt,lll l,lll r,lll x,int y){
if (!rt) rt=++tot; w[rt]+=y;
if (l==r) return;
lll mid=(l+r)>>1;
if (x<=mid) update(ls[rt],l,mid,x,y);
else update(rs[rt],mid+1,r,x,y);
}
int query(int rt,lll l,lll r,lll x,lll y){
if (!rt) return 0;
if (l==x&&r==y) return w[rt];
lll mid=(l+r)>>1;
if (y<=mid) return query(ls[rt],l,mid,x,y);
else if (x>mid) return query(rs[rt],mid+1,r,x,y);
else return query(ls[rt],l,mid,x,mid)+query(rs[rt],mid+1,r,mid+1,y);
}
int main(){
Q=iut(),d=iut();
K.insert(-INF),K.insert(INF);
F.insert(-INF),F.insert(INF);
update(root,-INF,INF,-INF,1);
update(root,-INF,INF,INF,1);
for (int i=1;i<=Q;++i)
if (iut()==2){
x=iut(),it=F.lower_bound(x);
nxt=*it,--it,pre=*it;
print(query(root,-INF,INF,pre,nxt)-1),putchar(10);
}else{
x=iut(),it=K.lower_bound(x);
if ((*it)==x){
F.erase(x);
++it,nxt=*it,--it,--it,pre=*it;
if (nxt-pre>d) F.insert(pre);
K.erase(x),update(root,-INF,INF,x,-1);
}else{
nxt=*it,--it,pre=*it,F.erase(pre);
if (nxt-x>d) F.insert(x);
if (x-pre>d) F.insert(pre);
K.insert(x),update(root,-INF,INF,x,1);
}
}
return 0;
}
ABC356 G - Freestyle
分析
有一些姿势虽然能游更长,但更耗费体力,有些虽然不怎么耗费体力,但是游得不够久。
考虑对 (体力,距离) 维护上凸壳,其实答案很接近斜率,如果体力完全够用,
那么使用最后一个点,否则二分斜率的位置解二元一次方程即可。
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=200011;
struct Point{
long long x,y;
Point operator -(const Point &t)const{
return (Point){x-t.x,y-t.y};
}
}a[N];
int n,st[N],Top,mx;
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
bool cmp(Point x,Point y){
if (x.x!=y.x) return x.x<y.x;
return x.y>y.y;
}
long long cj(Point x,Point y){
return x.x*y.y-x.y*y.x;
}
int main(){
n=iut();
for (int i=1;i<=n;++i) a[i]=(Point){iut(),iut()};
sort(a,a+1+n,cmp),Top=1;
for (int i=1;i<=n;++i)
if (a[i].x!=a[i-1].x){
while (Top>1&&cj(a[st[Top]]-a[st[Top-1]],a[i]-a[st[Top-1]])>=0) --Top;
st[++Top]=i;
}
for (int i=1;i<=Top;++i)
if (mx<a[st[i]].y) mx=a[st[i]].y;
while (Top&&a[st[Top]].y<mx) --Top;
for (int Q=iut();Q;--Q){
int x=iut(),y=iut();
if (x*a[st[2]].y<y*a[st[2]].x){
printf("-1\n");
continue;
}
if (x*a[st[Top]].y>=y*a[st[Top]].x){
printf("%.12Lf\n",(long double)y/a[st[Top]].y);
continue;
}
int l=2,r=Top;
while (l<r){
int mid=(l+r+1)>>1;
if (x*a[st[mid]].y>=y*a[st[mid]].x) l=mid;
else r=mid-1;
}
printf("%.12Lf\n",(long double)cj((Point){x,y},a[st[l]]-a[st[l+1]])/cj(a[st[l+1]],a[st[l]]));
}
return 0;
}