OI 笑传 #23

今天是 ABC429 CDEF。被 E 卡到破防说是。代码能力场。

ABC429C

给三元组的样子分个类,\(AAB,ABB,ABA\) 这三种。

对于前两种,用个桶前缀后缀一下算贡献即可。

对于中间的,我们动态维护每种数左边和右边数量的乘积,因为每次只会改一个数,维护是简单的。

剩下就是代码了,一遍写对。

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;
}
const int N=2e5+5;
i64 a[N];
i64 t[N];
i64 t1[N];
i64 t2[N];
pair<i64,i64> t3[N];
int main(){
  
  int n;cin>>n;
  for(int i=1;i<=n;i++){
    cin>>a[i];t[a[i]]++;
  }
  i64 ans=0;
  for(int i=1;i<=n;i++){
    t1[a[i]]++;
    if(t1[a[i]]>=2){
      ans+=(t1[a[i]]-1)*(n-i-(t[a[i]]-t1[a[i]]));
    }
    t3[i].second=t[i];
  }
  for(int i=n;i>=1;i--){
    t2[a[i]]++;
    if(t2[a[i]]>=2){
      ans+=(t2[a[i]]-1)*(i-1-(t[a[i]]-t2[a[i]]));
    }
  }
  i64 msum=0;
  for(int i=1;i<=n;i++){
    msum-=t3[a[i]].first*t3[a[i]].second;
    ans+=msum;
    t3[a[i]].first++;
    t3[a[i]].second--;
    msum+=t3[a[i]].first*t3[a[i]].second;
  }
  cout<<ans;
  
  return 0;
}

ABC429D

朴素二分题,首先离散化,把每个放了人的位置的答案计算出来即可。

注意环的处理和相邻人的位置贡献的计算。

赛时好像这题也卡飞不少人?

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;
}
const int N=6e5+5;
map<i64,int> mp;
int r[N];
i64 pre[N*2];
i64 po[N];
i64 ans[N];
i64 getsum(int len,int p){
  return pre[p+len-1]-pre[p-1];
}
int main(){

  i64 n,m,c;cin>>n>>m>>c;
  for(int i=1;i<=n;i++){
    i64 p;cin>>p;
    mp[p]++;
  }
  int cnt=0;
  for(pair<i64,int> v:mp){
    cnt++;
    r[cnt]=v.second;
    po[cnt]=v.first;
    pre[cnt]=pre[cnt-1]+r[cnt];
  }
  for(int i=cnt+1;i<=2*cnt;i++){
    pre[i]=pre[i-1]+r[i-cnt];
  }
  for(int i=1;i<=cnt;i++){
    int l=0,r=cnt+1;
    while(l<r){
      int mid=(l+r)>>1;
      if(getsum(mid,i)<c)l=mid+1;
      else r=mid;
    }
    ans[i]=getsum(l,i);
  }
  int lst=0;
  i64 qans=0;
  for(int i=1;i<=cnt;i++){
    qans=qans+(po[i]-lst)*ans[i];
    lst=po[i];
  }
  qans+=(m-lst)*ans[1];
  cout<<qans;
  
  return 0;
}

ABC429E

真正的红如温。后 50min 全是这个题。

你说得对但是 5min 想懂了再 5min 就写完了。但是它就是不过啊。

普通 BFS 题,每个 S 算贡献即可。用 BFS 处理出每个点到一个 S 的最短路和不同 S 的非严格次短路。

但就是这种东西我也能写挂把。

赛后又写了一遍就过了,我咋看不出这两种写法的区别呢?

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;
}
const int N=3e5;
vector<int> e[N];
int d1[N],d2[N];
int f1[N],f2[N];
struct work{
  int v;
  int w;
  int id;
};
queue<work> q;
int main(){
  
  int n;cin>>n;
  int m;cin>>m;
  for(int i=1;i<=m;i++){
    int u,v;cin>>u>>v;
    e[u].push_back(v);
    e[v].push_back(u);
  }
  string s;cin>>s;
  s=' '+s;
  memset(d1,0x3f,sizeof d1);
  memset(d2,0x3f,sizeof d2);
  for(int i=1;i<=n;i++){
    if(s[i]=='S'){
      q.push(work{i,0,i});
      f1[i]=i;d1[i]=0;
    }
  }
  while(q.size()){
    int u=q.front().v;
    int w=q.front().w;
    int id=q.front().id;
    q.pop();
    w++;
    for(int v:e[u]){
      if(d1[v]==0x3f3f3f3f){
        d2[v]=d1[v];d1[v]=w;
        f1[v]=f2[v];f1[v]=id;
      }
      else if(d2[v]==0x3f3f3f3f&&f1[v]!=id){
        d2[v]=w;f2[v]=id;
      }
      else continue;
      q.push(work{v,w,id});
    }
  }
  for(int i=1;i<=n;i++){
    if(s[i]=='D'){cout<<d1[i]+d2[i]<<'\n';}
  }

  return 0;
}

ABC429F

线段树题。

首先观察性质,由于行只有三行,我们一定不会去往左移动的。

于是我们从左上到右下的路径一定可以被拆成许多段横着的线段,任意两个线段的交最多是 \(1\),也就是在同一列中换行的情况。

不只是左上到右下这两点,对于里面的每一个点对我们都能这么拆。

于是我们考虑把它设成状态,设 \(c_{i,j}\) 表示在某一个列区间里面,从最左边一列 \(i\) 行的点到最右边一列 \(j\) 行的点的最小步数。

然后你会发现两个相邻区间是可以合并的,就是类似 Floyd 一样,我们枚举这两个区间从那一行并起来的路径就行了。

于是这东西放到线段树上就对了。仅需单点修改和全局查询。

有意思的小技巧。

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;
#define INF 0x3f3f3f3f
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;
}
const int N=2e5+5;
string s[5];
struct seg{
  int l;int r;
  int c[4][4];
}t[N<<2];
void b(int p,int l,int r){
  t[p].l=l;t[p].r=r;
  for(int i=1;i<=3;i++)
  for(int j=1;j<=3;j++){
    t[p].c[i][j]=INF;
  }
  if(l==r){
    for(int i=1;i<=3;i++)for(int j=1;j<=3;j++){
      bool ok=1;
      for(int k=min(i,j);k<=max(i,j);k++){
        if(s[k][l]=='#')ok=0;
      }
      if(ok)t[p].c[i][j]=abs(i-j);
      else t[p].c[i][j]=INF;
    }
    return ;
  }
  int mid=l+r>>1;
  b(ls,l,mid);b(rs,mid+1,r);
  for(int k=1;k<=3;k++)
  for(int i=1;i<=3;i++)
  for(int j=1;j<=3;j++){
    t[p].c[i][j]=min({0ll+t[ls].c[i][k]+t[rs].c[k][j]+1,0ll+t[p].c[i][j],0ll+INF});
  }
  return ;
}
void upd(int p,int pos,int x){
  for(int i=1;i<=3;i++)
  for(int j=1;j<=3;j++){
    t[p].c[i][j]=INF;
  }
  if(t[p].l==t[p].r&&t[p].l==pos){
    if(s[x][pos]=='.'){
      s[x][pos]='#';
      for(int i=1;i<=3;i++)for(int j=1;j<=3;j++){
        bool ok=1;
        for(int k=min(i,j);k<=max(i,j);k++){
          if(s[k][pos]=='#')ok=0;
        }
        if(ok)t[p].c[i][j]=abs(i-j);
        else t[p].c[i][j]=INF;
      }
    }
    else{
      s[x][pos]='.';
      for(int i=1;i<=3;i++)for(int j=1;j<=3;j++){
        bool ok=1;
        for(int k=min(i,j);k<=max(i,j);k++){
          if(s[k][pos]=='#')ok=0;
        }
        if(ok)t[p].c[i][j]=abs(i-j);
        else t[p].c[i][j]=INF;
      }
    }
    return ;
  }
  int mid=t[p].l+t[p].r>>1;
  if(pos<=mid)upd(ls,pos,x);
  if(mid<pos) upd(rs,pos,x);

  for(int k=1;k<=3;k++)
  for(int i=1;i<=3;i++)
  for(int j=1;j<=3;j++){
    t[p].c[i][j]=min({0ll+t[ls].c[i][k]+t[rs].c[k][j]+1,0ll+t[p].c[i][j],0ll+INF});
  }
  return ;
}
seg qry(){return t[1];}
int main(){
  
  int n;cin>>n;
  cin>>s[1]>>s[2]>>s[3];
  s[1]=' '+s[1];s[2]=' '+s[2];s[3]=' '+s[3];
  int q;cin>>q;
  b(1,1,n);
  for(int i=1;i<=q;i++){
    int x,y;cin>>x>>y;
    upd(1,y,x);
    if(qry().c[1][3]>=INF)cout<< -1<<'\n';
    else cout<<qry().c[1][3]<<'\n';
  }
  
  return 0;
}

posted @ 2025-10-26 17:22  hm2ns  阅读(20)  评论(0)    收藏  举报