AtCoder Beginner Contest 237 题解

写篇题解证明我还在 OI 的世界里活着(

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

只有 ABCDEFG 的题解,H 不会。

A

模拟。

Code
void mian(){
  ll n;scanf("%lld",&n);
  if(-2147483648<=n&&n<=2147483647)puts("Yes");
  else puts("No");
}

B

模拟。但是一个比较恶心的东西是 \(1\le H,W\le 10^5\) 然后 \(HW\le 10^5\)。其实我们只需要把所有数存到一个一维数组里,然后写一个 get(x,y) 表示 \((x,y)\) 在一维数组里的位置就行了。

Code
const int N=1e5;
 
int a[N+10],b[N+10];
int n,m;
 
int get1(int i,int j){return (i-1)*m+j;}
int get2(int i,int j){return (j-1)*n+i;}
 
void mian(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
      scanf("%d",&a[get1(i,j)]);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
      b[get2(i,j)]=a[get1(i,j)];
  for(int i=1;i<=n*m;i++){
    printf("%d ",b[i]);
    if(i%n==0)puts("");
  }
}

C

其实所有的字符串都可以化归成这种情况:中间是一个字符串,前面是 \(cnt_1\) 个 \(\texttt a\),后面是 \(cnt_2\) 个 \(\texttt a\)。

显然 \(cnt_1\gt cnt_2\) 时是不行的(因为不能删字符),否则看中间那个字符串是不是回文的即可。

Code
const int N=1e6;
 
char s[N+10];
 
bool check(int l,int r){
  for(int i=l;i<=r;i++)
    if(s[i]!=s[r-i+l])return 0;
  return 1;
}
 
void mian(){
  scanf("%s",s+1);
  int n=int(strlen(s+1)),l=1,r=n;
  while(s[l]=='a')l++;
  while(s[r]=='a')r--;
  // [1,l-1] [l,r] [r+1,n]
  if(l-1>n-r)return puts("No"),void();
  if(check(l,r))puts("Yes");
  else puts("No");
}

D

正着做很麻烦,考虑反着做。

然后题就变成这样了:先插入一个 \(n\),如果 \(s_i=\texttt L\),则在最右边插入 \(i-1\),否则在最左边插入 \(i-1\),最后输出这个数列。

用双端队列实现即可。

Code
const int N=5e5;
 
char s[N+10];
 
void mian(){
  int n;scanf("%d",&n);
  scanf("%s",s+1);
  std::deque<int> q;
  q.push_back(n);
  for(int i=n;i>=1;i--)
    if(s[i]=='L')q.push_back(i-1);
    else q.push_front(i-1);
  for(int i=1;i<=n+1;i++){
    int x=q.front();q.pop_front();
    printf("%d ",x);
  }
  puts("");
}

E

对于每一条边 \((u,v)\),不妨设 \(H_u\ge H_v\),连一条权值为 \(H_u-H_v\) 的有向边 \(\langle u,v\rangle\),再连一条权值为 \(2(H_v-H_u)\) 的有向边 \(\langle v,u\rangle\)。然后我们从 \(1\) 出发用 SPFA 跑一遍最长路即可。

(不知道为什么 SPFA 没被卡)

Code
const int N=2e5;
const int M=2e5;
 
struct Edge{int to,nxt,w;}e[M*2+10];int head[N+10],tote;
inline void addEdge(int u,int v,int w){e[++tote]={v,head[u],w};head[u]=tote;}
 
int n,m,a[N+10];
ll dis[N+10];bool vis[N+10];
 
void SPFA(int s){
  memset(dis,~0x3f,sizeof(dis));dis[s]=0;
  std::queue<int> q;q.push(s);
  while(!q.empty()){
    int u=q.front();q.pop();
    vis[u]=0;
    for(int i=head[u];i;i=e[i].nxt){
      int v=e[i].to;
      if(dis[v]<dis[u]+e[i].w){
        dis[v]=dis[u]+e[i].w;
        if(!vis[v])q.push(v),vis[v]=1;
      }
    }
  }
}
 
void mian(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)
    scanf("%d",a+i);
  for(int i=1;i<=m;i++){
    int u,v;scanf("%d%d",&u,&v);
    if(a[u]<a[v])std::swap(u,v);
    addEdge(u,v,a[u]-a[v]);
    addEdge(v,u,2*(a[v]-a[u]));
  }
  SPFA(1);
  ll ans=0;
  for(int i=1;i<=n;i++)ans=std::max(ans,dis[i]);
  printf("%lld\n",ans);
}

F

看到如此小的数据范围,考虑 dp。

设 \(f_{i,j,k,l}\) 表示前 \(i\) 项,所有长度为 \(1,2,3\) 的上升子序列中结尾的最小值分别为 \(j,k,l\) 时(若不存在这样的上升子序列,则相应的值为 \(0\))的答案。

枚举序列的第 \(i\) 项为 \(p\),对 \(j,k,l,p\) 的大小关系分类讨论进行转移。

具体过程见代码。

Code
const int N=1000;
const int M=10;
const int P=998244353;
 
// f[i][j][k][l] -> 1~i, IS of length 1,2,3 end with j,k,l (as small as possible)
int n,m,f[N+10][M+5][M+5][M+5];
 
void mian(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++)f[1][i][0][0]=1;
  for(int i=2;i<=n;i++)
    for(int j=1;j<=m;j++)
      for(int k=0;k<=m;k++)
        for(int l=0;l<=m;l++)
          for(int p=1;p<=m;p++){
            // 先把不可能出现的情况排除掉
            if(j&&k&&l&&l<p)continue; // |LIS|=4
            if(!k&&l)continue; // 存在 |LIS|=3 但不存在 |LIS=2|
            if(k&&j>=k)continue; // 长度为 1 的 IS 的结尾比长度为 2 的大
            if(l&&k>=l)continue; // 长度为 2 的 IS 的结尾比长度为 3 的大
            if(!k){
              if(j<p)(f[i][j][p][0]+=f[i-1][j][0][0])%=P; // j<p, p 接 j 后面,出现长度为 2 的 IS
              else (f[i][p][0][0]+=f[i-1][j][0][0])%=P; // p<=j, p 作为一个新的 IS
            }else if(!l){
              if(k<p)(f[i][j][k][p]+=f[i-1][j][k][0])%=P; // j<k<p, p 接 k 后面,出现长度为 3 的 IS
              else if(j<p)(f[i][j][p][0]+=f[i-1][j][k][0])%=P; // j<p<=k, p 接 j 后面
              else (f[i][p][k][0]+=f[i-1][j][k][0])%=P; // p<=j<k, p 作为一个新的 IS
            }else{
              if(k<p)(f[i][j][k][p]+=f[i-1][j][k][l])%=P; // j<k<p<l, p 接 k 后面
              else if(j<p)(f[i][j][p][l]+=f[i-1][j][k][l])%=P; // j<p<=k<l, p 接 j 后面
              else (f[i][p][k][l]+=f[i-1][j][k][l])%=P; // p<=j<k<l, p 作为一个新的 IS
            }
          }
  int ans=0;
  for(int i=1;i<=m;i++)
    for(int j=i+1;j<=m;j++)
      for(int k=j+1;k<=m;k++)
        (ans+=f[n][i][j][k])%=P;
  printf("%d\n",ans);
}

G

待补。

上一篇:田捷、沈定刚、张益肇、王熙的医学影像 AI 观丨CCF-GAIR 2019


下一篇:在Windows中以编程方式禁用密码复杂性