2024 ICPC Asia Chengdu Regional Contest(LJABGI)

2024 ICPC Asia Chengdu Regional Contest(LJABGI)

L. Recover Statistics

签到题 .


输出50个a , 45个b ,4个c , 1个c+1 即可.

J. Grand Prix of Ballance

模拟题.


注意开long long

A. Arrow a Row

构造题.

通过操作\(***...***\rightarrow\)>-...->>> , (长度需大于5) ,构造出题目给出的字符串.


最后三个字符一定是">>>",第一个一定是">" ,一定有"-",必要性显然 ,下面的构造方法能证明充分性. 找到最后一个"-".

先将字符串变为">-...->>>...>>"

中间的">"从前往后一个一个加入.

G. Expanding Array

位运算 , 思维题.


只用考虑相邻两个数能构造出来的数. 可以猜想能构造出来的东西会很少.可以尝试暴力枚举.但常数过大.

考虑优化一下 , 假设前面几个操作能构造出全部答案 , 我们不妨算一下时间能过的前提下最多进行几次.

可以算出是3次. 然后就过了()

auto calc=[&](int a,int x,int b) {
    if (x==0) {
      return a|b;
    }else if (x==1) {
      return a&b;
    }else {
      return a^b;
    }
  };
  auto getval=[&](int x,int y,int p) {
    if (p) return x;
    return y;
  };
  auto work=[&](int x,int y) {
    for (int a1=0;a1<2;++a1) {
      int res1=getval(x,y,a1);
      st.insert(res1);
      for (int s1=0;s1<3;++s1) {
        for (int a2=0;a2<2;++a2) {
          int res2=calc(res1,s1,getval(x,y,a2));
          st.insert(res2);
          for (int s2=0;s2<3;++s2) {
            for (int a3=0;a3<2;++a3) {
              int res3=calc(res2,s2,getval(x,y,a3));
              st.insert(res3);
            }
          }
        }
      }
    }
  };

考虑证明 :

proof:

由于任意一个式子只由2个数字决定 , 不妨按位考虑 , 对于某个确定的式子 , 具体的一位只由x 和 y 的取值有关.

称让某个式子的某一位为1 的,x,y的取值为一个规则. 一共有16个规则. 但对于含有x=0 且y=0的这一个规则,可以特殊处理,因为0一定能被构造出来 . 此时还剩8种规则 . 我们可以全部构造出来.0,x,y,x|y,x&y,x^y,(x|y)^x,(x|y)^y

I. Good Partitions

简单数学, 数据结构 ,


这道题本来很快想到线段树维护gcd的做法 , 但不知道为什么想可行性的时候想成区间修改了 , 虽然也能实现 , 但是没想出办法.....

First

所以这里有一个不用线段树的解法,时间复杂度\(O(n\log n)\).

很容易想到答案是将数组划分成不减段后,除去最后一段的段长度的最大公因数的因数个数 . 由于段长很小 , 每次只会改变最多两个段, 我们可用两个数组计数 , 一个记录某个因子出现次数 , 一个记录出现次数的次数 . 最后的答案就是出现次数为段数减1的次数.比较显然.

代码细节比较多 , 下面对代码实现比较困难的一些地方和坑点进行说明 .

  • 对于段长度的记录 , 我们可以用set记录断点即满足\(a[p]<a[p-1]\)的位置 , 再把1和n+1放进去,这样能保证set不会越界.

  • 进行插入 , 删除操作时,判断清楚.

#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 2e5 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;
const int MX=2e5;
int cnt1[N],cnt2[N];

vector<int> d[N];

void Insert(int n) {
  // cout<<"insert "<<n<<endl;
  for (auto x: d[n]) {
    cnt2[cnt1[x]]--;
    cnt2[cnt1[x]+1]++;
    cnt1[x]++;
  }
}

void Erase(int n) {
  // cout<<"erase "<<n<<endl;
  for (auto x: d[n]) {
    cnt2[cnt1[x]]--;
    cnt2[cnt1[x]-1]++;
    cnt1[x]--;
  }
}

void solve() {
  int n,q;cin>>n>>q;
  multiset<int> L;
  vector<int> a(n+1) ;
  for (int i=1;i<=n;++i) cin>>a[i],cnt1[i]=cnt2[i]=0;
  cnt2[0]=n;
  for (int i=1;i<n;++i) {
    if (a[i]>a[i+1]) L.insert(i+1);
  }
  L.insert(1),L.insert(n+1);
  auto it=next(L.begin());
  while (it!=L.end()) {
    if (*it!=n+1) Insert(*it-*prev(it));
    it=next(it);
  }
  cout<<cnt2[L.size()-2]<<"\n";
  while (q--) {
    int p,v;cin>>p>>v;
    //p , v;
    auto r=L.end(),l=L.begin();
    if (p>1 and L.count(p) and v>=a[p-1]) {
      L.erase(p);
      r=L.upper_bound(p),l=prev(r);
      if (*r!=n+1) Insert(*r-*l);
      Erase(p-*l);
      if (*r!=n+1) Erase(*r-p);
    }
    if (p<n and L.count(p+1) and a[p+1]>=v) {
      L.erase(p+1);
      r=L.upper_bound(p+1),l=prev(r);
      if (*r!=n+1) Insert(*r-*l);
      Erase(p+1-*l);
      if (*r!=n+1) Erase(*r-p-1);
    }
    if (p>1 and v<a[p-1] and L.count(p)==0) {
      r=L.upper_bound(p),l=prev(r);
      if (*r!=n+1) Erase(*r-*l);
      Insert(p-*l);
      if (*r!=n+1) Insert(*r-p);
      L.insert(p);
    }
    if (p<n and v>a[p+1] and L.count(p+1)==0) {
      r=L.upper_bound(p+1),l=prev(r);
      if (*r!=n+1) Erase(*r-*l);
      Insert(p+1-*l);
      if (*r!=n+1) Insert(*r-p-1);
      L.insert(p+1);
    }
    a[p]=v;
    cout<<cnt2[L.size()-2]<<"\n";
  }
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
  freopen("C:\\ACM\\code\\test.in", "r", stdin);
  // freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endif
  for (int i = 1; i <= MX; ++i) {
    for (int j = i; j <= MX; j += i) {
      d[j].push_back(i);
    }
  }
  int Case = 1;
  cin >> Case;
  while (Case--) solve();
  return 0;
}

注意到 若我们把索引减一 , 就可以只维护下标的因数, 会好写很多.

void solve() {
  int n,q;cin>>n>>q;
  set<int> L;
  vector<int> a(n+1) ;
  for (int i=1;i<=n;++i) cin>>a[i],cnt1[i]=cnt2[i]=0;
  for (int i=1;i<n;++i) {
    if (a[i]>a[i+1]) L.insert(i+1);
  }
  auto it=L.begin();
  while (it!=L.end()) {
    Insert(*it-1);
    it=next(it);
  }
  if (L.empty()) cout<<n<<"\n";
  else cout<<cnt2[L.size()]<<"\n";
  while (q--) {
    int p,v;cin>>p>>v;
    //p , v;
    if (p>1 and L.count(p) and v>=a[p-1]) {
      L.erase(p);
      Erase(p-1);
    }
    if (p<n and L.count(p+1) and a[p+1]>=v) {
      L.erase(p+1);
      Erase(p);
    }
    if (p>1 and v<a[p-1] and L.count(p)==0) {
      L.insert(p);
      Insert(p-1);
    }
    if (p<n and v>a[p+1] and L.count(p+1)==0) {
      L.insert(p+1);
      Insert(p);
    }
    a[p]=v;
    if (L.empty()) cout<<n<<"\n";
    else cout<<cnt2[L.size()]<<"\n";
  }
}

Second

这里是线段树维护区间gcd的写法.

单点修改 , 区间查询gcd

#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 2e5 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;

int mygcd(int a,int b) {
  return __gcd(abs(a),abs(b));
}
template<class T>
struct Segt {
  struct node {
    int l,r;
    T w;// gcd
    T sum;
  };
  vector<T> w;
  vector<node> t;
  Segt(){}
  Segt(int n) {init(n);}
  Segt(vector<int> in) {
    int n=in.size()-1;
    w.resize(n+1);
    for (int i=1;i<=n;++i) {
      w[i]=in[i];
    }
    init(in.size()-1);
  }
#define GL k<<1
#define GR k<<1|1
  void init(int n) {
    t.resize(4*n +1);
    auto build=[&](auto self ,int l, int r,int k=1) {
      if (l==r) {
        t[k]={l,r,w[l],w[l]};
        return ;
      }
      t[k]={l,r,0,0};
      int mid=(l+r)/2;
      self(self,l,mid,GL);
      self(self,mid+1,r,GR);
      pushup(k);
    };
    build(build,1,n);
  }
  void pushup(int k) {
    auto pushup=[&](node& p,node& l, node &r) {
      p.w=mygcd(l.w,r.w);
      p.sum=l.sum+r.sum;
    };
    pushup(t[k],t[GL],t[GR]);
  }
  void add(int pos,T val,int k=1) {
    if (t[k].l==t[k].r) {
      t[k].w+=val;
      t[k].sum+=val;
      return ;
    }
    int mid=(t[k].l+t[k].r)/2;
    if (pos<=mid) add(pos,val,GL);
    else add(pos,val,GR);
    pushup(k);
  }
  // 单点赋值, 不用管sum
  void upd(int pos,T val,int k=1) {
    if (t[k].l==t[k].r) {
      t[k].w=val;
      return ;
    }
    int mid=(t[k].l+t[k].r)/2;
    if (pos<=mid) upd(pos,val,GL);
    else upd(pos,val,GR);
    pushup(k);
  }
  T askgcd(int l,int r,int k=1) {
    if (l<=t[k].l and t[k].r<=r) return t[k].w;
    int mid=(t[k].l+t[k].r)/2;
    T ans=0;
    if (l<=mid) ans=mygcd(ans,askgcd(l,r,GL));
    if (mid<r)  ans=mygcd(ans,askgcd(l,r,GR));
    return ans;
  }
  T asksum(int l,int r,int k=1) {
    if (l<=t[k].l and t[k].r<=r) return t[k].sum;
    int mid=(t[k].l+t[k].r)/2;
    T ans=0;
    if (l<=mid) ans+=asksum(l,r,GL);
    if (mid<r)  ans+=asksum(l,r,GR);
    return ans;
  }
};

vector<int> pri;
bitset<N> not_pri;
int d[N],g[N];

void Euler(int n=2e5) {
  d[1]=1;
  for (int i=2;i<=n;++i){
    if (!not_pri[i]) {
      pri.push_back(i);
      d[i]=2;
      g[i]=1;
    }
    for (auto j:pri) {
      if (i*j>n) break;
      not_pri[i*j]=1;
      if (i%j==0) {
        g[i * j] = g[i] + 1;
        d[i * j] = (g[i * j] + 1) * d[i] / (g[i] + 1);
        break;
      }else {
        g[i*j]=1;
        d[i*j]=d[i]*d[j];
      }
    }
  }
}


void solve() {
  int n,m;cin>>n>>m;
  vector<int> a(n+1),b(n+1);
  for (int i=1;i<=n;++i) cin>>a[i];
  for (int i=2;i<=n;++i) {
    if (a[i]<a[i-1]) b[i]=i-1;//索引减一
    // cout<<b[i]<<"\n";
  }
  Segt<int> sgt(b);
  d[0]=n;
  cout<<d[sgt.askgcd(1,n)]<<"\n";
  for (int i=1;i<=m;++i) {
    int p,v;cin>>p>>v;
    if (p>1 and v<a[p-1]) sgt.upd(p,p-1);
    if (p<n and a[p+1]<v) sgt.upd(p+1,p);
    if (p>1 and v>=a[p-1]) sgt.upd(p,0);
    if (p<n and a[p+1]>=v) sgt.upd(p+1,0);
    a[p]=v;
    cout<<d[sgt.askgcd(1,n)]<<"\n";
    a[p]=v;
  }
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
  freopen("C:\\ACM\\code\\test.in", "r", stdin);
  // freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endif
  int Case = 1;
  cin >> Case;
  Euler();
  while (Case--) solve();
  return 0;
}

B. Athlete Welcome Ceremony

dp , 前缀和


首先dp比较显然 , 但因为有多组询问 , 直接dp显然不行.我们考虑每组询问有没有重合的部分, 发现可以先确定一种使用情况的方案数 , 再做一个前缀和就可以了.

具体地 , 我们很先计算出恰好用了\((x,y,z)\)\((a,b,c)\)的方案数 , 然后做一个3维前缀和. 具体实现见代码, 这里简单说一下容易出错的部分.

  • 一定要限制\(a+b+c=cnt\) ,故\(c<0\)就不用管了
  • 以及转移时不要遗漏
#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 1e9+7;
const int N = 1e6 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;
int dp[2][310][310][3];
int f[310][310][310];
void solve() {
  int n,q;cin>>n>>q;
  string s;cin>>s;
  s="@"+s;
  int cnt=0;
  for (int i=1;i<=n;++i) {
    if (s[i]=='?') cnt++;
    if (i==1) {
      if (s[1]=='?') {
        for (int j=0;j<3;++j) {
          if (i<n and s[i+1]-'a'==j) continue;
          if (j==0) dp[1][1][0][0]=1;
          if (j==1) dp[1][0][1][1]=1;
          if (j==2) dp[1][0][0][2]=1;
        }
      }else {
        dp[1][0][0][s[1]-'a']=1;
      }
      continue;
    }
    for (int a=0;a<=300;++a) {
      for (int b=0;b<=300;++b) {
        int c=cnt-a-b;
        if (c<0) break;
        int now=i&1,lst=now^1;
        for (int j=0;j<3;++j) dp[now][a][b][j]=0;
        int k=s[i]-'a';
        if (s[i]!='?') {
          for (int j=0;j<3;++j) {
            if (k!=j) {
              dp[now][a][b][k]+=dp[lst][a][b][j],dp[now][a][b][k]%=mod;
              // cout<<i<<" "<<a<<" "<<b<<" "<<k<<" "<<dp[now][a][b][k]<<endl;
            }
          }
        }else {
          for (int j=0;j<3;++j) {
            if (i<n and s[i+1]-'a'==j) continue;
            for (int t=0;t<3;++t) {
              if (j==t) continue;
              if (j==0 and a) dp[now][a][b][j]+=dp[lst][a-1][b][t],dp[now][a][b][j]%=mod;
              if (j==1 and b) dp[now][a][b][j]+=dp[lst][a][b-1][t],dp[now][a][b][j]%=mod;
              if (j==2 and c) dp[now][a][b][j]+=dp[lst][a][b][t],dp[now][a][b][j]%=mod;
            }
            // cout<<i<<" "<<a<<" "<<b<<" "<<c<<" "<<j<<" "<<dp[now][a][b][j]<<endl;
          }
        }
      }
    }
  }
  // cout<<cnt<<"\n";
  for (int a=0;a<=300;++a) {
    for (int b=0;b<=300;++b) {
      for (int c=0;c<=300;++c) {
        if (a+b+c==cnt) {
          for (int i=0;i<3;++i) {
            f[a][b][c]+=dp[n&1][a][b][i];
            f[a][b][c]%=mod;
          }
          // if (f[a][b][c]) cout<<a<<" "<<b<<" "<<c<<" "<<f[a][b][c]<<"\n";
        }
      }
    }
  }
  for (int a=1;a<=300;++a) {
    for (int b=0;b<=300;++b) {
      for (int c=0;c<=300;++c) {
        f[a][b][c]+=f[a-1][b][c];
        f[a][b][c]%=mod;
      }
    }
  }
  for (int a=0;a<=300;++a) {
    for (int b=1;b<=300;++b) {
      for (int c=0;c<=300;++c) {
        f[a][b][c]+=f[a][b-1][c];
        f[a][b][c]%=mod;
      }
    }
  }
  for (int a=0;a<=300;++a) {
    for (int b=0;b<=300;++b) {
      for (int c=1;c<=300;++c) {
        f[a][b][c]+=f[a][b][c-1];
        f[a][b][c]%=mod;
      }
    }
  }
  for (int i=1;i<=q;++i) {
    int x,y,z;cin>>x>>y>>z;
    cout<<f[x][y][z]<<"\n";
  }
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
  freopen("C:\\ACM\\code\\test.in", "r", stdin);
  // freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endif
  int Case = 1;
  // cin >> Case;
  while (Case--) solve();
  return 0;
}

posted @ 2025-04-10 20:44  _lull  阅读(150)  评论(0)    收藏  举报