OI 笑传 #30

怎么用 suzume 当摘要阅读这么多,suzume桑是你引的流吗。。。那我再试试。

今天是 ABC432 的 CDEF。超绝注意力场+码力场。

赛时发现 C 比较问号于是去看 DE,结果心血来潮想去把 D 写了,结果还真写出来了。50min 写了 4.1kb。然后就是不想再去写 E 了。

赛后发现真有几位仁兄和我一样只过了ABD,还不如先写 E。但还是要夸夸自己把 D 模拟出来了。

代码明天补。

ABC432C

赛时第一想法是假设每个人糖果数为 \(C\),然后可以设给第 \(i\) 个小孩 \(a_i\) 个小糖,\(b_i\) 个大糖。然后就能把 \(a_i,b_i\) 解出来:

\[b_i=\frac{C-XA_i}{Y-X} \]

\[a_i=\frac{XYA_i+C^2+C-YC-XA_i}{X(Y-X)} \]

然后我就一直在问号接下来怎么办。

就是注意到你如果把任两个小孩的大糖果数相减,\(C\) 就没了,差是个定值。

\[b_i-b_j=\frac{X(A_j-A_i)}{Y-X} \]

我也想有这么好的注意力,但感觉还是挺常规的吧。

既然任意两个人拿的大糖果数差一定,那找个领头的吧。

注意到上面是形如 \(A_j-A_i\) 的形式,我们想让上面是正的,于是我们让 \(A_i\)\(A\) 中的最小值,于是其它小孩和这个最小 \(A\) 的小孩的大糖果数量差就是个定值了,接下来让这个小孩的 \(b_i\) 最大即可,也就是 \(b_i=A_i\)

这样其他人的糖果数量就都是最大的了。

实现上可以先给 \(A\) 串排个序。

ABC432D

注意到 \(N\) 很小啊.于是想怎么搞都行。

发现操作的本质是平移,那我们维护整块矩形即可。平移可能做的就是拆分矩形或者只是平移。

判断连通块的时候,其实可以用扫描线做,但是我不会啊。于是就把矩形向四个边框抽出来,塞到行列对应的 map 里去。之后枚举每个矩形的相邻行列,看看线段是否有交就行了,合并用并查集即可。

然后就是把这东西实现出来了:

code

Show me the code
#define rd read()
#define mkp make_pair
#define ls p<<1
#define rs p<<1|1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#include<bits/stdc++.h>
using namespace std;
typedef long long i64;
typedef unsigned long long u64;
typedef unsigned int u32;
typedef __int128 i128;
i64 read(){
  i64 x=0,f=1;
  char c=getchar();
  while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
  while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
  return x*f;
}
struct block{
  i64 x1,y1;
  i64 x2,y2;
  int prd;
};
deque<block> b;
struct work{
  i64 l;i64 r;
  int id;
  bool operator<(const work &x)const{
    if(l==x.l)return r<x.r;
    else return l<x.l;
  }
};
map<i64,vector<work> > col,row;
i64 siz[131072];
int fa[131072];
int _find(int u){
  return fa[u]==u?u:fa[u]=_find(fa[u]);
}
map<int,int> r;
i64 ans[131072];
int main(){
  
  int n;cin>>n;
  i64 x,y;cin>>x>>y;
  b.push_back(block{0,0,x-1,y-1,0});
  for(int i=1;i<=n;i++){
    char c;cin>>c;
    if(c=='X'){
      i64 x,dy;cin>>x>>dy;
      while(b.front().prd!=i){
        block t=b.front();b.pop_front();
        // t.x1 ~ t.x2
        if(t.x2<x||x<=t.x1){
          if(t.x2<x){
            t.y1-=dy;t.y2-=dy;
          }
          else{
            t.y1+=dy;t.y2+=dy;
          }
          t.prd=i,b.push_back(t);
          continue;
        }
        i64 upx2=x-1,downx1=x;
        block t1=block{t.x1,t.y1-dy,upx2,t.y2-dy,i};
        block t2=block{downx1,t.y1+dy,t.x2,t.y2+dy,i};
        b.push_back(t1);b.push_back(t2);
      }
    }
    if(c=='Y'){
      i64 y,dx;cin>>y>>dx;
      while(b.front().prd!=i){
        block t=b.front();b.pop_front();
        // t.y1 ~ t.y2
        if(t.y2<y||y<=t.y1){
          if(t.y2<y){
            t.x1-=dx;t.x2-=dx;
          }
          else{
            t.x1+=dx;t.x2+=dx;
          }
          t.prd=i,b.push_back(t);continue;
        }
        i64 lefty2=y-1,righty1=y;
        block t1=block{t.x1-dx,t.y1,t.x2-dx,lefty2,i};
        block t2=block{t.x1+dx,righty1,t.x2+dx,t.y2,i};
        b.push_back(t1);b.push_back(t2);
      }
    }
  }
  vector<block> bn;
  int cnt=-1;
  while(b.size()){
    cnt++;
    block t2=b.front();
    bn.push_back(t2);
    b.pop_front();
    i64 x1=t2.x1,y1=t2.y1,x2=t2.x2,y2=t2.y2;
    siz[cnt]=llabs(x2-x1+1)*llabs(y2-y1+1);
    row[y1].push_back(work{x1,x2,cnt});
    row[y2].push_back(work{x1,x2,cnt});
    col[x1].push_back(work{y1,y2,cnt});
    col[x2].push_back(work{y1,y2,cnt});
  }
  for(int i=0;i<=cnt;i++)fa[i]=i;
  // for(pair<i64,vector<work> > k:row){
  //   sort(row[k.first].begin(),row[k.first].end());
  // }
  for(int i=0;i<=cnt;i++){
    i64 x1=bn[i].x1,y1=bn[i].y1,x2=bn[i].x2,y2=bn[i].y2;
    if(col.find(x1-1)!=col.end()){
      for(work c:col[x1-1]){
        if(y1<=c.l&&c.l<=y2 || c.l<=y1&&y1<=c.r){
          int t1=_find(i),t2=_find(c.id);
          if(t1!=t2){
            fa[t1]=t2;
          }
        }
      }
    }
    if(col.find(x2+1)!=col.end()){
      for(work c:col[x2+1]){
        if(y1<=c.l&&c.l<=y2 || c.l<=y1&&y1<=c.r){
          int t1=_find(i),t2=_find(c.id);
          if(t1!=t2){
            fa[t1]=t2;
          }
        }
      }
    }
    if(row.find(y1-1)!=row.end()){
      for(work c:row[y1-1]){
        if(x1<=c.l&&c.l<=x2 || c.l<=x1&&x1<=c.r){
          int t1=_find(i),t2=_find(c.id);
          if(t1!=t2){
            fa[t1]=t2;
          }
        }
      }
    }
    if(row.find(y2+1)!=row.end()){
      for(work c:row[y2+1]){
        if(x1<=c.l&&c.l<=x2 || c.l<=x1&&x1<=c.r){
          int t1=_find(i),t2=_find(c.id);
          if(t1!=t2){
            fa[t1]=t2;
          }
        }
      }
    }
  }
  int ccc=0;
  for(int i=0;i<=cnt;i++){
    int t=_find(i);
    if(r.find(t)==r.end()){
      ccc++;
      r[t]=ccc;
      ans[ccc]+=siz[i];
    }
    else{
      ans[r[t]]+=siz[i];
    }
  }
  cout<<ccc<<'\n';
  sort(ans+1,ans+1+ccc);
  for(int i=1;i<=ccc;i++){
    cout<<ans[i]<<' ';
  }
  
  return 0;
}

ABC432E

常规题吧,观察这个 \(\max \min\) 状物会把序列分成前半 \(l\) 中间 \(A_i\) 后面 \(r\) 的样子,这里序列是有序的。

于是算权值就直接找到有多少个 \(A_i\)\(l\) 小,多少个比 \(r\) 大就是,还要带修,这不就平衡树干的事?

但是我并不会写平衡树,也不想去用 pbds,我就在想还能怎么做?

想了一会儿发现我咋不会啊。才看见这 \(x,y\) 咋都这么小,这不用线段树状数组随便做。

但写完 D 已经还剩 20min 了,现在还有点累,就不写了吧(

ABC432F

牛题。

有解无解很容易判吧,就是求个和看能否整除。

然后想想该怎么刻画这个移糖果的过程。

首先一个想法是把小孩分成多的和少的两组,让多的给少的。

但是怎么给?顺序是可能对总操作数有影响的。

然后又有一个很重要的观察就是我们可以先把多的糖果聚到一块,在依次发给少的。

你可能问这样也不一定最优啊,但我们可保证的是这样的操作数一定是 人数﹣1.也就是说我们定住了答案的上界。

于是那怎么让答案更优?你注意这个上界能取到只要里面的数求个和再除数量等于全体的平均值就行。于是?

我们试试把序列分成尽可能多的,平均值等于总平均值的可重集组,就是答案了。

话这么说,你怎么证明要是分不出来了,这样移动的操作数是上界同时也是下界?

首先如果一个可重集还能分成两个平均值等于总平均值的可重集,显然可以少一次操作吧。

如果不能分出来,考虑我们一对一的给糖果,多个给少的,给不完再来一个少的,给不够再来一个多的。

你会发现这样至少两人之间会互相给一次,而且因为这里面不能再分出来平均值等于总平均值的可重集(但它自己是),这样互相给的过程不会中断。

于是下界就是 人数-1 了。

分序列的过程可以用状压 DP 做,具体就是枚举状态,当状态满足平均值等于总平均值时让增量为 \(1\),否则为 \(0\),之后枚举上一个加入的数进行转移,加上增量,时间复杂度 \(O(N2^N)\)


G 似乎是 NTT 板题,但我不会 NTT 啊。

posted @ 2025-11-18 23:49  hm2ns  阅读(18)  评论(0)    收藏  举报