其他分享
首页 > 其他分享> > AtCoder Beginner Contest 253 题解

AtCoder Beginner Contest 253 题解

作者:互联网

二模考完了,打场比赛放松身心。

比赛地址:https://atcoder.jp/contests/abc253

只有 ABCDEF 的题解,G 待补,H 不会。

A

模拟。

Code
void mian(){
  int a,b,c;
  scanf("%d%d%d",&a,&b,&c);
  if(a<=b&&b<=c||c<=b&&b<=a)puts("Yes");
  else puts("No");
}

B

还是模拟。

Code
void mian(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)
    scanf("%s",s[i]+1);
  int x1=-1,y1=-1,x2=-1,y2=-1;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
      if(s[i][j]=='o'){
        if(x1==-1)x1=i,y1=j;
        else x2=i,y2=j;
      }
  printf("%d\n",std::abs(x1-x2)+std::abs(y1-y2));
}

C

又是模拟。

Code
void mian(){
  int m;scanf("%d",&m);
  std::multiset<int> s;
  while(m--){
    int opt,x,y;scanf("%d",&opt);
    if(opt==1)scanf("%d",&x),s.insert(x);
    if(opt==2){
      scanf("%d%d",&x,&y);
      for(int i=1;i<=y;i++){
        auto it=s.find(x);
        if(it==s.end())break;
        s.erase(it);
      }
    }
    if(opt==3){
      printf("%d\n",*(--s.end())-*(s.begin()));
    }
  }
}

D

小学奥数容斥原理。

Code
ll sum(ll x){
  return 1LL*x*(x+1)/2;
}
 
void mian(){
  ll n,a,b;
  scanf("%lld%lld%lld",&n,&a,&b);
  ll lcm=a/std::__gcd(a,b)*b;
  printf("%lld\n",sum(n)-a*sum(n/a)-b*sum(n/b)+(lcm)*sum(n/lcm));
}

E

dp。设 \(f_{i,j}\) 表示考虑前 \(i\) 项且 \(a_i=j\) 时的答案。则

\[f_{i,j}=\sum_{|j-l|\ge k}f_{i-1,l} \]

显然可以通过对于每个 \(i\) 处理 \(f_{i,j}\) 的前缀和来优化。

注意特判 \(k=0\) 的情况!

Code
const int P=998244353;
const int N=5000;
 
int n,m,k,f[N+10][N+10],sum[N+10][N+10];
 
void mian(){
  scanf("%d%d%d",&n,&m,&k);
  for(int i=1;i<=m;i++)f[1][i]=1,sum[1][i]=(sum[1][i-1]+1)%P;
  for(int i=2;i<=n;i++){
    if(k){
      for(int j=1;j<=m;j++){
        if(j-k>=1)(f[i][j]+=sum[i-1][j-k])%=P;
        if(j+k<=m)(f[i][j]+=(sum[i-1][m]-sum[i-1][j+k-1])%P)%=P;
      }
    }else{
      for(int j=1;j<=m;j++)
        (f[i][j]+=sum[i-1][m])%=P;
    }
    for(int j=1;j<=m;j++)
      sum[i][j]=(sum[i][j-1]+f[i][j])%P;
  }
  int ans=0;
  for(int i=1;i<=m;i++)
    (ans+=f[n][i])%=P;
  printf("%d\n",(ans%P+P)%P);
}

F

这题出的不错。

考虑对于一个询问 \((x,y)\),什么修改操作对它有贡献。

那显然是离它最近且修改的是第 \(x\) 行的那个操作 \(2\) 和这个操作 \(2\) 之后,所有涉及到第 \(y\) 列的操作 \(3\)。

前者开 \(n\) 个 std::vector 维护即可。

对于后者,本质上是有两个轴:时间轴和列轴。这样一来,我们可以以时间轴为要被可持久化的轴,建一棵可持久化线段树,维护区间加、单点查。注意要开标记永久化。

Code
#include<algorithm>
#include<cstdio>
#include<vector>

typedef long long ll;

const int N=2e5;

struct Node{
  ll v,lt;
  int ls,rs;
};

int n,m,q;
Node t[N<<6];
int rt[N+10],cnt;
std::vector<std::pair<int,int>> vec[N+10];

#define ls(x) (t[x].ls)
#define rs(x) (t[x].rs)

void pushUp(int i,int l,int r){
  int mid=(l+r)>>1;
  t[i].v=t[ls(i)].v+t[rs(i)].v+t[ls(i)].lt*(mid-l+1)+t[rs(i)].lt*(r-mid);
}

void modify(int& i,int j,int l,int r,int ml,int mr,int d){
  i=++cnt;
  t[i].v=t[j].v,t[i].lt=t[j].lt;
  ls(i)=ls(j),rs(i)=rs(j);
  if(ml<=l&&r<=mr){
    t[i].lt+=d;
    return;
  }
  int mid=(l+r)>>1;
  if(ml<=mid)modify(ls(i),ls(j),l,mid,ml,mr,d);
  if(mr>mid) modify(rs(i),rs(j),mid+1,r,ml,mr,d);
  pushUp(i,l,r);
}

ll query(int i,int l,int r,int ql,int qr){
  if(ql==l&&r==qr)return t[i].v+t[i].lt*(r-l+1);
  int mid=(l+r)>>1;
  ll tag=(qr-ql+1)*t[i].lt;
  if(ql>mid) return tag+query(rs(i),mid+1,r,ql,qr);
  if(qr<=mid)return tag+query(ls(i),l,mid,ql,qr);
  return tag+query(ls(i),l,mid,ql,mid)+query(rs(i),mid+1,r,mid+1,qr);
}

#undef ls
#undef rs

int main(){
  scanf("%d%d%d",&n,&m,&q);
  for(int i=1;i<=n;i++)vec[i].push_back({1,0});
  for(int i=1;i<=q;i++){
    int opt,l,r,x,y;
    scanf("%d",&opt);
    if(opt==1){
      scanf("%d%d%d",&l,&r,&x);
      modify(rt[i],rt[i-1],1,m,l,r,x);
    }
    if(opt==2){
      scanf("%d%d",&l,&x);
      rt[i]=rt[i-1];
      vec[l].push_back({i,x});
    }
    if(opt==3){
      scanf("%d%d",&x,&y);
      rt[i]=rt[i-1];
      std::pair<int,int> qwq=vec[x].back();
      int t=qwq.first,x0=qwq.second;
      ll sum=query(rt[i],1,m,y,y)-query(rt[t-1],1,m,y,y);
      printf("%lld\n",x0+sum);
    }
  }
}

标签:AtCoder,rs,int,题解,sum,scanf,lt,253,ll
来源: https://www.cnblogs.com/registergen/p/abc253_solution.html