Atcoder Regular Contest 160

· 题解

开始写at

A - Reverse and Count

给定一个 1∼N1\sim N的排列AA,定义f(L,R)f(L,R)为reverse AL,…,ARA_L,\dots,A_R部分得到的新排列,于是有N(N+1)2\frac{N(N+1)}{2}个f(L,R)f(L,R),求字典序第kk大的新排列

依次考虑第 ii 位,排列从小到大,一定是将 [i+1,n][i+1,n] 中比 p[i]p[i] 小的数字与其替换,再者考虑固定了第 ii 位之后剩余的 n−in-i 位,再考虑 [i+1,n[i+1,n 中比 p[i]p[i] 大的数字与其替换

复杂度 O(N2)O(N^2)

#include<bits/stdc++.h>
using namespace std;
int main(){
  int n,k;
  cin>>n>>k;
  vector<int>p(n);
  for(int &x:p)cin>>x;
  int a=1,b=n*(n+1)/2;
  for(int i=0;i<n;++i){
    vector<int>low,high;
    for(int j=i+1;j<n;++j)
      if(p[j]<p[i])low.push_back(p[j]);
      else high.push_back(p[j]);
    int x=-1;
    if(k-a<(int)low.size()){
      sort(low.begin(),low.end());
      x=low[k-a];
    }
    if(b-k<(int)high.size()){
      sort(high.begin(),high.end(),greater<int>());
      x=high[b-k];
    }
    if(x!=-1){
      int j=i;
      while(p[j]!=x)++j;
      reverse(p.begin()+i,p.begin()+j+1);
      break;
    }
    a+=low.size(),b-=high.size();
  }
  for(int &x:p)
    cout<<x<<' ';
  return 0;
}

B - Triple Pair

多测

给定一个整数 NN,求满足条件 (xy≤n,zx≤n,yz≤n)(xy\le n,zx\le n,yz\le n) 的三元组 (x,y,z)(x,y,z) 数量,答案对 998244353998244353 取模

T≤100,1≤N≤109T\le 100, 1\le N\le 10^9

分类讨论得答案 n3+∑i=n+1n3×ni\sqrt{n}^3+\sum\limits_{i=\sqrt{n}+1}^n{3\times\frac{n}{i}}

但如果只是循环还是会T,所以要上数论分块

#include <bits/stdc++.h>
using i64=long long;
const i64 mod = 998244353;
void solve() {
  int n;
  std::cin >> n;
  int m = sqrt(n);
  i64 ans = ((1ll * m * m) % mod) * m % mod;
  for (int i = m + 1, j; i <= n; i = j + 1) {
    int v = n / i;
    j = n / v;
    ans = (ans + ((1ll * v * v) % mod) * 3 * (j - i + 1) % mod) % mod;
  }
  std::cout << ans << '\n';
}
int main() {
  int tt;
  std::cin >> tt;
  while (tt--) {
    solve();
  }
}

顺便贴一份Jiangly的 modInt 板子,不过赛场上不太可能用得到就是了

template<class T>
constexpr T power(T a, i64 b) {
  T res = 1;
  for (; b; b /= 2, a *= a) {
    if (b % 2) {
      res *= a;
    }
  }
  return res;
}

constexpr i64 mul(i64 a, i64 b, i64 p) {
  i64 res = a * b - i64(1.L * a * b / p) * p;
  res %= p;
  if (res < 0) {
    res += p;
  }
  return res;
}
template<i64 P>
struct MLong {
  i64 x;
  constexpr MLong() : x{} {}
  constexpr MLong(i64 x) : x{norm(x % getMod())} {}

  static i64 Mod;
  constexpr static i64 getMod() {
    if (P > 0) {
      return P;
    } else {
      return Mod;
    }
  }
  constexpr static void setMod(i64 Mod_) {
    Mod = Mod_;
  }
  constexpr i64 norm(i64 x) const {
    if (x < 0) {
      x += getMod();
    }
    if (x >= getMod()) {
      x -= getMod();
    }
    return x;
  }
  constexpr i64 val() const {
    return x;
  }
  explicit constexpr operator i64() const {
    return x;
  }
  constexpr MLong operator-() const {
    MLong res;
    res.x = norm(getMod() - x);
    return res;
  }
  constexpr MLong inv() const {
    assert(x != 0);
    return power(*this, getMod() - 2);
  }
  constexpr MLong &operator*=(MLong rhs) & {
    x = mul(x, rhs.x, getMod());
    return *this;
  }
  constexpr MLong &operator+=(MLong rhs) & {
    x = norm(x + rhs.x);
    return *this;
  }
  constexpr MLong &operator-=(MLong rhs) & {
    x = norm(x - rhs.x);
    return *this;
  }
  constexpr MLong &operator/=(MLong rhs) & {
    return *this *= rhs.inv();
  }
  friend constexpr MLong operator*(MLong lhs, MLong rhs) {
    MLong res = lhs;
    res *= rhs;
    return res;
  }
  friend constexpr MLong operator+(MLong lhs, MLong rhs) {
    MLong res = lhs;
    res += rhs;
    return res;
  }
  friend constexpr MLong operator-(MLong lhs, MLong rhs) {
    MLong res = lhs;
    res -= rhs;
    return res;
  }
  friend constexpr MLong operator/(MLong lhs, MLong rhs) {
    MLong res = lhs;
    res /= rhs;
    return res;
  }
  friend constexpr std::istream &operator>>(std::istream &is, MLong &a) {
    i64 v;
    is >> v;
    a = MLong(v);
    return is;
  }
  friend constexpr std::ostream &operator<<(std::ostream &os, const MLong &a) {
    return os << a.val();
  }
  friend constexpr bool operator==(MLong lhs, MLong rhs) {
    return lhs.val() == rhs.val();
  }
  friend constexpr bool operator!=(MLong lhs, MLong rhs) {
    return lhs.val() != rhs.val();
  }
};

template<>
i64 MLong<0LL>::Mod = 1;

template<int P>
struct MInt {
  int x;
  constexpr MInt() : x{} {}
  constexpr MInt(i64 x) : x{norm(x % getMod())} {}

  static int Mod;
  constexpr static int getMod() {
    if (P > 0) {
      return P;
    } else {
      return Mod;
    }
  }
  constexpr static void setMod(int Mod_) {
    Mod = Mod_;
  }
  constexpr int norm(int x) const {
    if (x < 0) {
      x += getMod();
    }
    if (x >= getMod()) {
      x -= getMod();
    }
    return x;
  }
  constexpr int val() const {
    return x;
  }
  explicit constexpr operator int() const {
    return x;
  }
  constexpr MInt operator-() const {
    MInt res;
    res.x = norm(getMod() - x);
    return res;
  }
  constexpr MInt inv() const {
    assert(x != 0);
    return power(*this, getMod() - 2);
  }
  constexpr MInt &operator*=(MInt rhs) & {
    x = 1LL * x * rhs.x % getMod();
    return *this;
  }
  constexpr MInt &operator+=(MInt rhs) & {
    x = norm(x + rhs.x);
    return *this;
  }
  constexpr MInt &operator-=(MInt rhs) & {
    x = norm(x - rhs.x);
    return *this;
  }
  constexpr MInt &operator/=(MInt rhs) & {
    return *this *= rhs.inv();
  }
  friend constexpr MInt operator*(MInt lhs, MInt rhs) {
    MInt res = lhs;
    res *= rhs;
    return res;
  }
  friend constexpr MInt operator+(MInt lhs, MInt rhs) {
    MInt res = lhs;
    res += rhs;
    return res;
  }
  friend constexpr MInt operator-(MInt lhs, MInt rhs) {
    MInt res = lhs;
    res -= rhs;
    return res;
  }
  friend constexpr MInt operator/(MInt lhs, MInt rhs) {
    MInt res = lhs;
    res /= rhs;
    return res;
  }
  friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
    i64 v;
    is >> v;
    a = MInt(v);
    return is;
  }
  friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
    return os << a.val();
  }
  friend constexpr bool operator==(MInt lhs, MInt rhs) {
    return lhs.val() == rhs.val();
  }
  friend constexpr bool operator!=(MInt lhs, MInt rhs) {
    return lhs.val() != rhs.val();
  }
};

template<>
int MInt<0>::Mod = 1;

template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();

constexpr int P = 998244353;
using Z = MInt<P>;

C - Power Up

给定长为 n≤2×105n\le 2\times 10^5 的序列 a,ai≤2×105a,a_i\le 2\times 10^5

允许做若干次操作,一次操作可以将两个 xx 变为一个 x+1x+1

问 AA 的方案数模 998244353

令 BxB_x 表示 xx 在 AA 中出现的次数,而 AA 中最大的数可以是 max⁡A+log⁡N\max{A}+\log{N}

考虑从小到大计数,这样不会漏掉可能的情况

  • 令 f[i][j]f[i][j] 为操作完 1∼i1\sim i 中的数后有 jj 个 i+1i+1 时, AA 的情况数

若操作完 1∼i−11\sim i-1 后至多有 kk 个 ii,即 1∼i−11\sim i-1 每个数字都至多保留一个,那么最多能产生 k+bi2\frac{k+b_i}{2} 个 i+1{i+1},即 f[i+1][(k+bi)/2]+=f[i][k]f[i+1][(k+b_i)/2]+=f[i][k],易得 ∀j∈[0,k+bi2]\forall j\in [0,\frac{k+b_i}{2}],都有 f[i+1][(j+bi)/2]+=f[i][j]f[i+1][(j+b_i)/2]+=f[i][j]

考虑保留一部分的 ii,那么能产生 jj 个 i+1i+1 的 AA 自然也能产生 j−1j-1 个 i+1i+1

dp用滚动数组压掉一维即可


考虑复杂度,其实就是将每次的计数次数求和

∑i=1max⁡A+log⁡N∑j=1iBj×2j2i\sum_{i=1}^{\max{A}+\log{N}}\sum_{j=1}^{i}\frac{B_j\times 2^j}{2^i}

交换求和号

  ⟺  ∑j=1max⁡A+log⁡N∑i=jmax⁡A+log⁡NBj(12)i−j≤∑j=1max⁡A+log⁡N2Bj≤2N\iff \sum_{j=1}^{\max{A}+\log{N}}\sum_{i=j}^{\max{A}+\log{N}}{B_j(\frac{1}{2})^{i-j}}\le \sum_{j=1}^{\max{A}+\log{N}}2B_j\le 2N

如果觉得不够直观,可以看我 树状数组博客 的最后那个图,本质其实是一样的。

#include<bits/stdc++.h>
using namespace std;
int n,t[201000],f[201000],g[401000];
const int N=2e5,mod=998244353;
int main(){
  cin>>n;
  for(int i=0,tem;i<n;++i)cin>>tem,t[tem]++;
	int sj=0;
	f[0]=1;
	for(int i=1;i<=N+200;i++){
		int Z=(sj+t[i])/2;
		for(int j=0;j<=Z;j++)g[j]=0;
		for(int j=0;j<=sj;j++)(g[(j+t[i])/2]+=f[j])%=mod;
		for(int j=Z-1;j>=0;j--)(g[j]+=g[j+1])%=mod;
		for(int j=0;j<=Z;j++)f[j]=g[j];
		sj=Z;
	}
  cout<<f[0]<<'\n';
}