2022“杭电杯”中国大学生算法设计超级联赛(9)题解报告

Problem J. Sum Plus Product

题解:

可以判断出\prod_{i=1}^{n}(a_{i}+1)是不变量,所以答案就是\prod_{i=1}^{n}(a_{i}+1) - 1。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 2e5 + 10;
const int mod = 998244353;

int n, a[N];

void solve() {
    cin >> n ;
    for(int i = 1; i <= n ; i ++) cin >> a[i];
    for(int i = 1; i < n; i ++){
        a[i + 1] = (a[i] * a[i + 1] % mod + a[i] + a[i + 1]) % mod;
    }
    cout << a[n] << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

 Problem H. Shortest Path in GCD Graph

题解:

做法. 首先最短路长度不超过2,因为对任意(𝑥, 𝑦),沿路径𝑥 → 1 → 𝑦即可得到一条长度为2的路径。最短路长度为1当且仅当𝑔𝑐𝑑 (𝑥, 𝑦) = 1,否则等价于询问[1, 𝑛]中满足𝑔𝑐𝑑 (𝑥, 𝑧) =1且𝑔𝑐𝑑 (𝑦, 𝑧) = 1的𝑧的数量,分解𝑥, 𝑦后用容斥可以解决。注意𝑔𝑐𝑑 (𝑥, 𝑦) = 2时,直接𝑥 → 𝑦也是一条长度为2的路径。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 2e5 + 10;
const int mod = 998244353;

int n, nn, q, ans;
vector<int> d;

int gcd(int x, int y){
    return x % y ? gcd(y, x % y) : y;
}

void init(int n){
    for(int i = 2; i * i <= n; i ++){
        if(n % i) continue;
        d.push_back(i);
        while(n % i == 0) n /= i;
    }
    if(n > 1) d.push_back(n);
}

void dfs(int ii, int cnt, int res){
    if(ii == nn){
        if(cnt == 0) return;
        if(cnt % 2) ans += n / res;
        else ans -= n / res;
        return;
    }
    dfs(ii + 1, cnt, res);
    dfs(ii + 1, cnt + 1, res * d[ii]);
}

void solve() {
    cin >> n >> q;
    int u, v;
    while(q--){
        cin >> u >> v;
        if(gcd(u, v) == 1) cout << "1 1\n";
        else{
            ans = 0;
            cout << "2 ";
            d.clear();
            init(u), init(v);
            sort(d.begin(), d.end());
            d.erase(unique(d.begin(), d.end()), d.end());
            nn = d.size();
            dfs(0, 0, 1);
            cout << n - ans + (gcd(u, v) == 2) << '\n';
        }
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t = 1;
    //cin >> t;
    while(t--){
        solve();
    }
}

Problem G. Matryoshka Doll

题解:

做法. 𝑑𝑝 (𝑥, 𝑦)表示将前𝑥个套娃分成𝑦组的方案数,转移为
𝑑𝑝 (𝑥, 𝑦) = 𝑑𝑝 (𝑥 − 1, 𝑦 − 1) + 𝑑𝑝 (𝑥 − 1, 𝑦) · max{0, 𝑦 − 𝑓 (𝑥)}.
其中𝑓 (𝑥)表示满足1 ≤ 𝑧 < 𝑥且𝑎𝑥 − 𝑟 < 𝑎𝑧 ≤ 𝑎𝑥 的𝑧的个数。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#include<set>
#include<stack>
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 5e3 + 10;
const int mod = 998244353;

int n, k, r, a[N], dp[N][N];

void solve()
{
    cin >> n >> k >> r;
    for(int i = 0; i <= n; i++){
        for(int j = 0; j <= k; j ++){
            dp[i][j] = 0;
        }
    }
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }
    int p = 0; 
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++){
        while(p + 1 < i && a[p + 1] + r <= a[i]) p ++;
        int s = i - p - 1;
        for(int j = s + 1; j <= i; j++){
            dp[i] [j] = ((j - s) * dp[i - 1][j] + dp[i - 1][j - 1]) % mod;
        }
    }
    cout << dp[n][k] << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t = 1;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem C. Fast Bubble Sort

题解:

假设𝑃 = 𝑛1𝜆1𝑛2𝜆2 · · · 𝑛𝑘 𝜆𝑘 ,则𝐵(𝑃) = 𝜆1𝑛1𝜆2𝑛2 · · · 𝜆𝑘𝑛𝑘 ,其中𝑛1, · · · , 𝑛𝑘 为从左到右的局
部最大值且有
𝑛1 < 𝑛2 < · · · < 𝑛𝑘,
则不难证明答案为非空𝜆𝑖 的个数。
将询问离线,每次从𝑛到1倒序扫描左端点𝑙并回答所有左端点为𝑙的询问。对于每个固定的左端
点𝑙,[𝑙, 𝑛]中从左到右的局部最大值可以通过单调栈维护,局部最大值插入/删除对于答案的影
响可以用树状数组/线段树快速更新/求解。

代码:

// #pragma comment(linker, "/STACK:102400000,102400000")
#pragma GCC optimize("O3")
#pragma GCC optimize("O2")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx")
// #pragma GCC optimize("trapv")
#include<bits/stdc++.h>
// #include <bits/extc++.h>
#define int long long
#define double long double
// #define i128 long long
// #define double long double
using namespace std;
 
#define rep(i,n) for (int i=0;i<(int)(n);++i)
#define rep1(i,n) for (int i=1;i<=(int)(n);++i)
#define range(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define pb push_back
#define F first
#define S second
 
typedef long long ll;
typedef unsigned long long ull;
// typedef long double ld;
typedef pair<int, int> pii;
typedef vector<int> vi;


namespace internal {

// @param n `0 <= n`
// @return minimum non-negative `x` s.t. `n <= 2**x`
int ceil_pow2(int n) {
    int x = 0;
    while ((1U << x) < (unsigned int)(n)) x++;
    return x;
}

// @param n `1 <= n`
// @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0`
// constexpr int bsf_constexpr(unsigned int n) {
//     int x = 0;
//     while (!(n & (1 << x))) x++;
//     return x;
// }

// @param n `1 <= n`
// @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0`
int bsf(unsigned int n) {
#ifdef _MSC_VER
    unsigned long index;
    _BitScanForward(&index, n);
    return index;
#else
    return __builtin_ctz(n);
#endif
}

}  // namespace internal


template <class S, S (*op)(S, S), S (*e)()> struct segtree {
  public:
    segtree() : segtree(0) {}
    segtree(int n) : segtree(std::vector<S>(n, e())) {}
    segtree(const std::vector<S>& v) : _n((int)(v.size())) {
        log = internal::ceil_pow2(_n);
        size = 1 << log;
        d = std::vector<S>(2 * size, e());
        for (int i = 0; i < _n; i++) d[size + i] = v[i];
        for (int i = size - 1; i >= 1; i--) {
            update(i);
        }
    }

    void set(int p, S x) {
        assert(0 <= p && p < _n);
        p += size;
        d[p] = x;
        for (int i = 1; i <= log; i++) update(p >> i);
    }

    S get(int p) {
        assert(0 <= p && p < _n);
        return d[p + size];
    }

    S prod(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        S sml = e(), smr = e();
        l += size;
        r += size;

        while (l < r) {
            if (l & 1) sml = op(sml, d[l++]);
            if (r & 1) smr = op(d[--r], smr);
            l >>= 1;
            r >>= 1;
        }
        return op(sml, smr);
    }

    S all_prod() { return d[1]; }

    template <bool (*f)(S)> int max_right(int l) {
        return max_right(l, [](S x) { return f(x); });
    }
    template <class F> int max_right(int l, F f) {
        assert(0 <= l && l <= _n);
        assert(f(e()));
        if (l == _n) return _n;
        l += size;
        S sm = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!f(op(sm, d[l]))) {
                while (l < size) {
                    l = (2 * l);
                    if (f(op(sm, d[l]))) {
                        sm = op(sm, d[l]);
                        l++;
                    }
                }
                return l - size;
            }
            sm = op(sm, d[l]);
            l++;
        } while ((l & -l) != l);
        return _n;
    }

    template <bool (*f)(S)> int min_left(int r) {
        return min_left(r, [](S x) { return f(x); });
    }
    template <class F> int min_left(int r, F f) {
        assert(0 <= r && r <= _n);
        assert(f(e()));
        if (r == 0) return 0;
        r += size;
        S sm = e();
        do {
            r--;
            while (r > 1 && (r % 2)) r >>= 1;
            if (!f(op(d[r], sm))) {
                while (r < size) {
                    r = (2 * r + 1);
                    if (f(op(d[r], sm))) {
                        sm = op(d[r], sm);
                        r--;
                    }
                }
                return r + 1 - size;
            }
            sm = op(d[r], sm);
        } while ((r & -r) != r);
        return 0;
    }

  private:
    int _n, size, log;
    std::vector<S> d;

    void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }
};

using S=int;
S op(S l,S r){return l+r;}
S e(){return 0;}


int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
const int mod=998244353;
const int base[]={12321,32123};
const double EPS=1e-9;
const double pi=acos(-1);
const int INF=1e18;
const int N=100017;
mt19937 rng(1235);

int n,q;
int p[N];
int ans[N],C[N],now[N];
vector<pii> info[N];
int st[N];

int lowbit(int u){return u&(-u);}
void update(int u,int w){for (int i=u;i<=n+7;i+=lowbit(i)) C[i]+=w;}
int query(int u){int ans=0; for (int i=u;i;i-=lowbit(i)) ans+=C[i]; return ans;}
signed main(){
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  int _;
  cin>>_;
  //_=1;
  while (_--){
    cin>>n>>q;
    rep(i,n) cin>>p[i];
    for (int i=1;i<=n+5;++i) C[i]=0,now[i]=0;
    rep(i,N) info[i].clear();
    p[n]=n+1;
    rep(i,q){
      int u,v;
      cin>>u>>v;
      u--, v--;
      info[u].pb({i,v});
    }
    int cnt=0;
    st[cnt++]=n; 
    auto upd=[&](int u){
      update(u+1,(now[u+1]?-1:1)), now[u+1]^=1;
      update(u+2,(now[u+2]?-1:1)), now[u+2]^=1;
    };
    for (int i=n-1;i>-1;--i){
      while (cnt&&p[i]>p[st[cnt-1]]) {
        cnt--; upd(st[cnt]);
      }
      st[cnt++]=i, upd(i);
      for (auto c:info[i]){
        int id=c.F, r=c.S;
        if (i==r) ans[id]=0;
        else ans[id]=(query(r+1)-query(i))/2;
      }
    }
    rep(i,q) cout<<ans[i]<<"\n";
  }
  return 0;
}

Problem A. Arithmetic Subsequence、

题解:

首先如果某个数出现次数大于等于3则不存在解。然后如果所有数字均为偶数,我们
可将所有数除以二;如果所有数字均为奇数,我们可将所有数减去一;否则,我们将所有奇数放在
左边,所有偶数放在右边,对奇数/偶数分治解决。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#include<set>
#include<map>
#include<stack>
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 5e3 + 10;
const int mod = 998244353;

int n;
vector<int> ans;

void solve()
{
    cin >> n;
    vector<int> a(n, 0);
    ans.clear();
    map<int, int> cnt;
    for(int i = 0; i < n; i ++) cin >> a[i];
    for(int i = 0; i < n; i++){
        cnt[a[i]] ++;
        if(cnt[a[i]] >= 3) {
            cout << "NO\n";
            return;
        }
    }
    cout << "YES\n";
    vector<pair<int, int>> v;
    for(int i = 0; i < n; i++){
        v.push_back({0, a[i]});
        for(int j = 0; j < 30; j ++){
            if(a[i] >> j & 1) v[i].first |= (1ll << (29 - j));
        }
    }
    sort(begin(v), end(v));
    for(int i = 0; i < n; i ++) ans.push_back(v[i].second);
    for(auto re : ans) cout << re << ' ';
    cout << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t = 1;
    cin >> t;
    while(t--){
        solve();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值