完整教程:GJOI 11.10 题解

1.AT_arc170_a Yet Another AB Problem

题意

在这里插入图片描述
在这里插入图片描述

思路

从前往后扫 s s s,记录 s i ≠ t i s_i\neq t_i si=ti 的位置上是 A 还是 BB 需要变成 AA 需要变成 B)。如果遇到一个 B 就先记录下来;如果遇到一个 A,就看前面有没有记录过(剩余)B,如果有就可以配成一对,操作一次 A B

于是最后会剩下一些 A 在前面和一些 B 在后面,那么只要最后一个(要被替换的) B 后面还有 s i = t i s_i=t_i si=tiB,和第一个 A 前面还有 A,就是有解的。

代码1(Atcoder WA on #18)

#include<bits/stdc++.h>
  using namespace std;
  #define ll long long
  const ll N=2e5+9;
  ll n;
  char s[N],t[N];
  ll pos1[N],pos2[N];
  int main()
  {
  freopen("replace.in","r",stdin);
  freopen("replace.out","w",stdout);
  scanf("%lld%s%s",&n,s+1,t+1);
  ll bta=0;
  ll atb=0;
  ll ans=0;
  for(int i=1;i<=n;i++)
  {
  if(s[i]!=t[i])
  {
  if(s[i]=='B')
  {
  pos1[++bta]=i;
  }
  else
  {
  if(bta)
  {
  s[pos1[bta]]='A',s[i]='B';
  ans++;
  bta--;
  }
  else pos2[++atb]=i;
  }
  }
  }
  if(atb==0&&bta==0)
  {
  printf("%lld",ans);
  return 0;
  }
  bool f1=0,f2=0;
  for(int i=pos1[bta]+1;i<=n;i++)
  if(s[i]=='B')f1=1;
  for(int i=1;i<pos2[1];i++)
  if(s[i]=='A')f2=1;
  if(f1&&f2)
  {
  printf("%lld",ans+atb+bta);
  }
  else puts("-1");
  return 0;
  }

但是这个代码总会被卡 1 ∼ 2 1\sim 2 12 个点呢。于是换了一个写法。

代码2(Atcoder AC)

#include<bits/stdc++.h>
  using namespace std;
  #define ll long long
  const ll N=2e5+9;
  ll n;
  char s[N],t[N];
  ll pos1[N],pos2[N];
  int main()
  {
  freopen("replace.in","r",stdin);
  freopen("replace.out","w",stdout);
  scanf("%lld%s%s",&n,s+1,t+1);
  for(int i=1;i<=n;i++)
  {
  if(t[i]=='A')break;
  if(s[i]=='A')
  {
  puts("-1");
  return 0;
  }
  }
  for(int i=n;i>=1;i--)
  {
  if(t[i]=='B')break;
  if(s[i]=='B')
  {
  puts("-1");
  return 0;
  }
  }
  ll ans=0;
  for(int i=1;i<=n;i++)
  ans+=(s[i]!=t[i]);
  ll cnt=0;
  for(int i=1;i<=n;i++)
  {
  if(s[i]==t[i])continue;
  if(t[i]=='A')cnt++;
  if(cnt&&t[i]=='B')ans--,cnt--;
  }
  printf("%lld",ans);
  return 0;
  }

2.洛谷 P12966 CCO2025 Asteroid Mining

题意

在这里插入图片描述
在这里插入图片描述

思路

赛时观察到质量种树只有 log ⁡ M = 40 \log M=40 logM=40 种也没什么头绪,只会拼命敲部分分呢打了 40pts 的 dp 呢。但是感觉 dp 没什么前途——毕竟这个数据给你,就不是用来 dp 的,于是考虑贪心。

按照质量排序,首先 m 1 ∣ m 2 ∼ n m_1|m_{2\sim n} m1m2n,所以 m 2 ∼ n m_{2\sim n} m2n 怎么组合,最后都会剩下 ⌊ M / m 1 ⌋   m o d   m 2 \left\lfloor M/m_1\right\rfloor \bmod m_2 M/m1modm2 的没法选,这部分全部用来选质量为 m 1 m_1 m1 的必然优的。

ll g=W/ww[i],meg=ww[i+1]/ww[i],vlen=vec[i].size();//当前可以选g个i质量;meg个i可以合成i+1 
sort(vec[i].begin(),vec[i].end(),cmp2);//贪心选多的
ll pre=min(vlen,(i==nn?g:g%meg));//g%meg个给后面也选不了,强制给i
ll pos=0;
for(;pos<pre;pos++)
{
ans+=vec[i][pos];
W--;
}
if(i==nn)break;

那么质量为 m 1 m_1 m1 的还有剩余怎么办?有一个精妙的解决方法,就是把上面代码的 m e g meg meg m 1 m_1 m1 合成一个 m 2 m_2 m2 质量的大块。

如果有合不到 m 2 m_2 m2 的怎么办呢?其实没有关系,假设最后一块没合完的为 m x m_x mx,因为 m 2 m_2 m2 成为当前最小时, ⌊ M ′ / m 2 ⌋   m o d   m 3 \left\lfloor M'/m_2\right\rfloor \bmod m_3 M/m2modm3 的余量,不会因为 m x < m 2 m_x<m_2 mx<m2 而装不下 m x m_x mx,于是是没有影响的。

代码

#include<bits/stdc++.h>
  using namespace std;
  #define ll long long
  const ll N=5e5+9;
  ll n,W;
  struct node
  {
  ll v,w;
  }a[N];
  bool cmp(node x,node y)
  {
  return x.w<y.w;
  }
  bool cmp2(ll x,ll y)
  {
  return x>y;
  }
  ll ww[N],nn;
  vector<ll>vec[N];
    int main()
    {
    freopen("mining.in","r",stdin);
    freopen("mining.out","w",stdout);
    scanf("%lld%lld",&n,&W);
    for(int i=1;i<=n;i++)
    {
    ll v,w;
    scanf("%lld%lld",&v,&w);
    a[i]=(node){v,w};
    ww[i]=w;
    }
    sort(a+1,a+n+1,cmp);
    ww[0]=1;
    for(int i=1;i<=n;i++)
    {
    if(a[i].w!=a[i-1].w)nn++;
    vec[nn].push_back(a[i].v);
    ww[nn]=a[i].w;
    }
    ll ans=0;
    for(int i=1;i<=nn;i++)
    {
    ll g=W/ww[i],meg=ww[i+1]/ww[i],vlen=vec[i].size();//当前可以选g个i质量;meg个i可以合成i+1 
    sort(vec[i].begin(),vec[i].end(),cmp2);
    ll pre=min(vlen,(i==nn?g:g%meg));//g%meg个给后面也选不了,强制给i
    ll pos=0;
    for(;pos<pre;pos++)
    {
    ans+=vec[i][pos];
    W--;
    }
    if(i==nn)break;
    for(;pos<vlen;)
    {
    ll meg_sum=0;
    for(int k=1;k<=meg&&pos<vlen;k++)//如果合成不到质量为ww[i+1]的就不管了 
    {
    meg_sum+=vec[i][pos];
    pos++;
    }
    vec[i+1].push_back(meg_sum);
    }
    }
    printf("%lld",ans);
    return 0;
    }

3.洛谷 P12914 POI2020 沙滩游客 / Plażowicze

题意

在这里插入图片描述
在这里插入图片描述

思路

赛时写了一个维护所有区间的大堆和分数的各种 operator,借用 O3 神力卡到 50pts。就是每次选一个长度最长的,尽量靠左的区间,位置选在中间,然后把该区间对半劈开。堆维护区间长度和左右端点。

但是正解其实就是暴力的一点小优化。因为劈开一次会多一个,所以 k ≤ 1 0 9 k\le 10^9 k109 的询问就会多 1 0 9 10^9 109 个。我们不想塞那么多东西到堆里。并且随着分裂次数增大,堆顶会有一堆长度相等的最大长度。

所以直接维护 3 3 3 个东西:分裂开的区间长度、原来的左端点和分裂的元素个数(右端点可不维护)。这样堆里始终只有 n n n 个元素,而 n n n 个元素里面劈开的小区间长度都相同,于是按照分裂区间长度为秩是正确的。根据新增分裂段数更新答案即可。时间复杂度 O ( min ⁡ ( n log ⁡ n , q ) ) O(\min(n\log n,q)) O(min(nlogn,q))

代码

#include<bits/stdc++.h>
  using namespace std;
  #define ll long long
  inline ll read()
  {
  ll s=0,w=1;
  char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
    }
    inline void write(ll x)
    {
    if(x==0){putchar('0');return;}
    ll len=0,k1=x,c[10005];
    if(k1<0)k1=-k1,putchar('-');
    while(k1)c[len++]=k1%10+'0',k1/=10;
    while(len--)putchar(c[len]);
    }
    const ll N=1e6+9;
    ll n,R,m;
    ll a[N];
    //-------↓分数类 -------
    struct Fs
    {
    ll fz,fm;
    void print()
    {
    ll g=__gcd(fz,fm);
    fz/=g,fm/=g;
    write(fz);
    printf("/");
    write(fm);
    puts("");
    }
    };
    inline Fs yf(Fs x)
    {
    Fs z=x;
    ll g=__gcd(z.fz,z.fm);
    z.fz/=g,z.fm/=g;
    return z;
    }
    inline Fs operator + (Fs x,Fs y)
    {
    Fs z={0,0};
    z.fm=max(x.fm,y.fm);
    z.fz=z.fm/x.fm*x.fz+z.fm/y.fm*y.fz;
    return z;
    }
    inline Fs operator * (Fs x,ll k)
    {
    Fs z=x;
    z.fz*=k;
    return z;
    }
    inline Fs operator / (Fs x,ll k)
    {
    Fs z=x;
    z.fm*=k;
    return z;
    }
    inline bool operator == (Fs x,Fs y)
    {
    return x.fz*y.fm==y.fz*x.fm;
    }
    inline bool operator < (Fs x,Fs y)
    {
    return x.fz*y.fm<y.fz*x.fm;
    }
    inline bool operator > (Fs x,Fs y)
    {
    return x.fz*y.fm>y.fz*x.fm;
    }
    //-------↑分数类 -------
    struct que
    {
    ll k,id;
    }Q[N];
    inline bool cmp(que x,que y)
    {
    return x.k<y.k;
    }
    struct node
    {
    Fs l,r,Dis;
    ll cnt;
    };
    inline bool operator < (node x,node y)
    {
    if(x.Dis==y.Dis)return x.l>y.l;
    return x.Dis<y.Dis;
    }
    priority_queue<node>q;
      Fs ANS[N];
      int main()
      {
      //	freopen("beach23.in","r",stdin);
      //	freopen("beach.out","w",stdout);
      n=read(),R=read(),m=read();
      for(int i=1;i<=n;i++)
      {
      a[i]=read();
      if(i>1)q.push((node){(Fs){a[i-1],1},(Fs){a[i],1},(Fs){a[i]-a[i-1],1},1});
      }
      for(int i=1;i<=m;i++)
      {
      ll k=read();
      Q[i]=(que){k,i};
      }
      sort(Q+1,Q+m+1,cmp);
      ll tick=0,pos=1;
      while(1)//tick分裂次数 
      {
      node tem=q.top();
      q.pop();
      ll l=tick+1,r;
      tick+=tem.cnt;
      r=tick;
      //加一轮/2的贡献,l分裂前r分裂后 
      while(pos<=m&&Q[pos].k<=r)
      {
      Fs Mid=(tem.l+tem.Dis*(Q[pos].k-l))+tem.Dis/2;
      ANS[Q[pos].id]=yf(Mid);
      pos++;
      }
      tem.Dis=tem.Dis/2;
      tem.cnt*=2;
      q.push(tem);
      if(pos>m||tick>=Q[m].k)break;
      }
      for(int i=1;i<=m;i++)
      ANS[i].print();
      return 0;
      }

4.洛谷 P8169 eJOI2021 Dungeons

先看着大佬的博客,待补。

posted @ 2026-01-24 14:14  clnchanpin  阅读(3)  评论(0)    收藏  举报