2019牛客多校 第一场 9/10

本文深入探讨了多项算法竞赛中的核心技巧与策略,包括区间划分、笛卡尔树、积分法求解、高维几何问题简化、概率计算及线性基应用等问题,并提供了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A

考虑最小值把区间疯狂划分,那就是说明笛卡尔树相同啦

/* ***********************************************
Author        :BPM136
Created Time  :7/18/2019 12:10:03 PM
File Name     :A.cpp
************************************************ */
 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<bitset>
#include<queue>
#include<ctime>
#include<set>
#include<map>
#include<list>
#include<vector>
#include<cassert>
#include<string>
#include<cinttypes>
#include<cstdint>
#include<array>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
 
using namespace std;
 
typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
 
int const N = 100005;
 
int v[N], ta[N], tb[N];
int fa[N][20], fb[N][20];
int a[N], b[N];
 
int RMQ(int R[], int f[N][20], int l,int r)
{
    int k=0;
    while (1<<(k+1)<=r-l+1) k++;
    int x=f[l][k],y=f[r-(1<<k)+1][k];
    return R[x]<R[y]?x:y;
}
 
void DP(int n, int f[N][20], int R[])
{
    for (int i=1;i<=n;i++) f[i][0]=i;
    for (int j=1;(1<<j)<=n;j++)
      for (int i=1;i+(1<<j)-1<=n;i++)
      {
          int x=f[i][j-1],y=f[i+(1<<(j-1))][j-1];
          f[i][j]=R[x]<R[y]?x:y;
      }
}
 
bool solve(int l, int r) {
    if (l >= r)
        return 1;
    auto posa = RMQ(a, fa, l, r);
    auto posb = RMQ(b, fb, l, r);
    if (posa != posb)
        return 0;
    return solve(l, posa - 1) && solve(posa + 1, r);
}
 
bool check(int lim) {
    DP(lim, fa, a);
    DP(lim, fb, b);
    return solve(1, lim);
}
 
int main() {
    int n;
    while (cin >> n) {
        for (int i = 1; i <= n; ++i)
            cin >> a[i];
        for (int i = 1; i <= n; ++i)
            cin >> b[i];
        int ans = 0, l = 1, r = n;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (check(mid)) {
                ans = mid;
                l = mid + 1;
            } else
                r = mid - 1;
        }
        cout << ans << '\n';
    }
    return 0;
}

B

大力积分,化一下式子,答案就会化成
∑i=1n1ai∏j=1,j!=inaj−ai∗(−1)n+1 \sum_{i=1}^n \frac{1}{ a_i \prod_{j=1,j != i}^{n} a_j-a_i} *(-1)^{n+1} i=1naij=1,j!=inajai1(1)n+1

/* ***********************************************
Author        :BPM136
Created Time  :7/18/2019 1:12:32 PM
File Name     :B.cpp
************************************************ */
 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<bitset>
#include<queue>
#include<ctime>
#include<set>
#include<map>
#include<list>
#include<vector>
#include<cassert>
#include<string>
#include<cinttypes>
#include<cstdint>
#include<array>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
 
using namespace std;
 
typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
 
ll const mod = 1e9 + 7;
 
ll KSM(ll a, ll k) {
    ll ret = 1;
    for ( ; k; k >>= 1, a = a * a % mod)
        if (k & 1)
            ret = ret * a % mod;
    return ret;
}
 
ll a[10005];
 
int main() {
    // cerr << KSM(5040, mod - 2) << '\n';
    int n;
    while (cin >> n) {
        for (int i = 1; i <= n; ++i)
            cin >> a[i];
        ll ans = 0;//-KSM(2, mod - 2);
        for (int i = 1; i <= n; ++i) {
            ll tmp = a[i];
            for (int j = 1; j <= n; ++j)
                if (i != j) {
                    tmp = tmp * (a[i] * a[i] % mod - a[j] * a[j] % mod) % mod;
                    tmp = (tmp + mod) % mod;
                }
            ans += KSM(tmp, mod - 2);
            ans %= mod;
        }
        if (n % 2 == 0)
            ans = -ans;
        cout << (ans * KSM(2, mod - 2) % mod + mod) % mod << '\n';
    }
    return 0;
}

C

虽然复杂度是错的,但是不知道为啥就过了x(来自小洛洛
每次投影到低一维的空间上,然后对中心点做垂直,发现不在面上就继续做垂直
可能出题人没有想过这种做法,于是就跑过去了,复杂度O(n2)O(n^2)O(n2)

#include <iostream>
#include <cstdint>
#include <algorithm>
#include <string>
#include <vector>
#include <cstdlib>
 
using i64 = int64_t;
// using i64 = __int128;
 
i64 gcd(i64 a, i64 b) {
    while (b != 0) {
        a %= b;
        std::swap(a, b);
    }
    return a;
}
 
struct Frac {
    i64 p, q;
 
private:
    Frac (i64 p, i64 q): p(p), q(q) {}
 
public:
    Frac (i64 p = 0): Frac(p, 1) {}
 
    static Frac from(i64 p, i64 q) {
        if (q < 0) {
            p = -p;
            q = -q;
        }
 
        auto g = gcd(p < 0 ? -p : p, q);
        return { p / g, q / g };
    }
 
    // Frac sqr () const {
    //     return { p * p, q * q };
    // }
 
    // Frac operator+ (const Frac &rhs) const {
    //     return from(p * rhs.q + q * rhs.p, q * rhs.q);
    // }
 
    // Frac operator- (const Frac &rhs) const {
    //     return from(p * rhs.q - q * rhs.p, q * rhs.q);
    // }
 
    // Frac operator/ (i64 t) const {
    //     return from(p, q * t);
    // }
 
    friend std::ostream &operator<< (std::ostream &os, const Frac &self) {
        return self.q == 1 ? os << (int64_t)self.p : os << (int64_t)self.p << '/' << (int64_t)self.q;
    }
};
 
std::vector<i64> solve(int n, int m, const std::vector<i64> &v, int &realn) {
    auto p = std::vector<i64>(n, 0);
    auto orz = std::vector<char>(n, 0);
 
    bool orzed;
    i64 s, curn;
    do {
        orzed = false;
        s = curn = 0;
        for (int i = 0; i < n; ++i)
            if (!orz[i]) {
                ++curn;
                s += v[i];
            }
 
        for (int i = 0; i < n; ++i)
            if (!orz[i]) {
                auto x = curn * v[i] + m - s;
                if (x < 0) {
                    orz[i] = orzed = true;
                    p[i] = 0;
                } else
                    p[i] = x;
            }
    } while (orzed);
 
    realn = curn;
    return p;
}
 
int main () {
    std::ios::sync_with_stdio(false);
 
    int n, m;
    while (std::cin >> n >> m) {
        auto v = std::vector<i64>(n);
        for (auto &a: v) {
            int t;
            std::cin >> t;
            a = t;
            // std::cin >> a;
        }
 
        int realn;
        auto p = solve(n, m, v, realn);
        i64 ans = 0;
        for (int i = 0; i < n; ++i) {
            auto c = p[i] - (i64)realn * v[i];
            ans += c * c;
        }
 
        std::cout << Frac::from(ans, (i64)realn * realn * m * m) << '\n';
    }
 
    return 0;
}

D

答案可以化为
12m∑i=1n∏j=1m(1−(−1)∣ai,j and x∣+1) \frac {1} {2 ^ m} \sum_{i = 1} ^ {n} \prod_{j = 1} ^ {m} (1 - (-1) ^ {|a_{i,j} \ and \ x| + 1}) 2m1i=1nj=1m(1(1)ai,j and x+1)
这里的∣a∣|a|a表示aaa的二进制中1的个数
上面这个等式来源于qls
然后他告诉我
∣a and b∣+∣c and b∣=∣(a xor c) and b∣ |a \ and \ b| + |c \ and \ b| = |(a \ xor \ c) \ and \ b| a and b+c and b=(a xor c) and b
那么我们把上面那个式子展开,就可以求啦
最后加上的
(−1)∣a xor x∣ (-1)^{|a \ xor \ x|} (1)a xor x
是FWT的系数
那么我们只要单独FWT一下就好了

#include <bits/stdc++.h>
  
using namespace std;
  
using ll = long long;
  
int const N = 2200005;
ll const mod = 1e9 + 7;
  
ll KSM(ll a, ll k) {
    ll ret = 1;
    for ( ; k; k >>= 1, a = a * a % mod)
        if (k & 1)
            ret = ret * a % mod;
    return ret;
}
  
ll F[N];
int a[11];
int n, m, K;
int val;
  
void dfs(int x, int one) {
    if (x == m) {
        F[val] += one;
        // cerr << val << ' ' << one << '\n';
        return;
    }
    int tmp = val;
    val ^= a[x];
    dfs(x + 1, -one);
    val = tmp;
    dfs(x + 1, one);
}
  
void FWT(ll *P, int len) {
    for (int i = 1; i < len; i <<= 1)
        for (int p = i << 1, j = 0; j < len; j += p)
            for (int k = 0; k < i; ++k) {
                int x = P[j + k], y = P[j + k + i];
                P[j + k] = 1ll*(x + y) % mod;
                P[j + k + i]=1ll*(x - y + mod) % mod;
            }
}
  
int main() {
    ios::sync_with_stdio(0);
    while (cin >> n >> m >> K) {
        for (int i = 0; i < (1 << K); ++i)
            F[i] = 0;
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j < m; ++j)
                cin >> a[j];
            dfs(0, 1);
        }
        // for (int i = 0; i < (1 << K); ++i)
        //     cerr << F[i] << ' ';
        // cerr << '\n';
        for (int i = 0; i < (1 << K); ++i)
            F[i] = (F[i] % mod + mod) % mod;
        FWT(F, 1 << K);
        // for (int i = 0; i < (1 << K); ++i)
        //     cerr << F[i] << ' ';
        // cerr << '\n';
        // cerr << (1 << m) << '\n';
        ll inv = KSM(1 << m, mod - 2);
        ll c = 1;
        int ans = 0;
        for (int i = 0; i < (1 << K); ++i) {
            ans ^= 1LL * c * F[i] % mod * inv % mod;
            c = c * 3 % mod;
        }
        cout << ans << '\n';
    }
}

E

这题非常的奥妙重重
就是给你一堆的AB和BA,然后插进去,问有多少个A和B构成的字符串满足可以被分成(可以是不连续的)长度为2的子序列,其中n个是AB,m个是BA
那我们考虑一个一个字符往里面填
假如这个字符是A,那么我们应该优先用AB里面的A
对于B来说就是优先用BA的B
那么任意时刻我们应该剩下一些A,B,AB,BA
那么我们填的A和B就应该按照刚刚的贪心顺序填。
那么我们就可以DP了

#include <bits/stdc++.h>
 
using namespace std;
 
int const N = 2005;
int const mod = 1e9 + 7;
 
int f[N][N];
 
inline void update(int& a, int b) {
    a += b;
    if (a >= mod)
        a -= mod;
}
 
int main() {
    int n, m;
    while (cin >> n >> m) {
        int s = (n + m);
        for (int i = 0; i <= s; ++i)
            for (int j = 0; j <= s; ++j)
                f[i][j] = 0;
        f[0][0] = 1;
        for (int i = 0; i <= s; ++i)
            for (int j = 0; j <= s; ++j)
                if (f[i][j]) {
                    int cnta = 0, cntb = 0;
                    int AB = max(n - i, 0);
                    int B = min(i, n) - max(j - m, 0);
                    cnta += AB;
                    cntb += max(B, 0);
                    int BA = max(m - j, 0);
                    int A = min(j, m) - max(i - n, 0);
                    cnta += max(A, 0);
                    cntb += BA;
                    // cerr << i << ' ' << j << ' ' << cnta << ' ' << cntb << '\n';
                    if (cnta > 0)
                        update(f[i + 1][j], f[i][j]);
                    if (cntb > 0)
                        update(f[i][j + 1], f[i][j]);
                }
        cout << f[s][s] << '\n';
    }
}

F

题解问小洛洛(((反正是水题

def main():
   try:
       while True:
           print(solve(*map(int, input().split(' '))))
   except EOFError as e:
       pass

def cross(x1, y1, x2, y2):
   return x1 * y2 - y1 * x2

def solve(x1, y1, x2, y2, x3, y3):
   S2 = abs(cross(x2 - x1, y2 - y1, x3 - x1, y3 - y1))
   return S2 * 11

main()

G

魔鬼题,待补

H

线性基题,首先我们先对n个数构建一个线性基B1,那么如果对于不构成线性基里面的数,肯定可以被线性基里面的数线性表出,那么方案自然是2n−r−12^{n-r-1}2nr1,这里的rrr是线性基的秩
那对于线性基里面的数呢?那就要考虑之前的时候这个数在刚刚的统计里面用了多少次,那也就是说,对于所有其他的数,能不能把这个数线性表出,如果可以自然和非基里面的答案一样,否则使用次数就是0,这里我们可以先对于其他的数构建一个线性基B2,每次把基里面的其他的数暴力插进去

#include <bits/stdc++.h>
 
using namespace std;
 
#define SZ(x) ((int)(x).size())
 
using ll = long long;
using ull = unsigned long long;
 
int const mod = 1e9 + 7;
 
ll KSM(ll a, ll k) {
    ll ret = 1;
    for (; k; k >>= 1, a = a * a % mod)
        if (k & 1)
            ret = ret * a % mod;
    return ret;
}
 
struct L_B {
    ull d[61], p[61];
    int cnt;
    L_B() {
        memset(d, 0, sizeof(d));
        memset(p, 0, sizeof(p));
        cnt = 0;
    }
    bool insert(ull val) {
        for (int i = 63; i >= 0; --i)
            if (val & (1ULL << i)) {
                if (!d[i]) {
                    d[i] = val;
                    break;
                }
                val ^= d[i];
            }
        return val > 0;
    }
};
 
L_B merge(L_B const& n1, L_B const& n2) {
    auto ret = n1;
    for (int i = 63; i >= 0; --i)
        if (n2.d[i])
            ret.insert(n2.d[i]);
    return ret;
}
 
int main() {
    ios::sync_with_stdio(0);
    int n;
    while (cin >> n) {
        auto a = vector<ull>(n);
        for (auto& v : a)
            cin >> v;
        auto B1 = L_B(), B2 = L_B();
        auto B1_pos = vector<int>();
        int ans = 0, r = 0;
        for (int i = 0; i < n; ++i)
            if (B1.insert(a[i])) {
                ++r;
                B1_pos.push_back(i);
            } else {
                B2.insert(a[i]);
                ++ans;
            }
        for (int i = 0; i < SZ(B1_pos); ++i) {
            auto tB = B2;
            for (int j = 0; j < SZ(B1_pos); ++j)
                if (i != j)
                    tB.insert(a[B1_pos[j]]);
            if (!tB.insert(a[B1_pos[i]]))
                ++ans;
        }
        cout << ans * KSM(2, n - r - 1) % mod << '\n';
    }
}

I

这种傻逼题我竟然在比赛的时候没有看到
把y离散了,然后大力DP就完事了

#include <bits/stdc++.h>
 
using namespace std;
 
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
 
using ll = long long;
 
inline ll max(ll const& a, ll const& b) {
    return a > b ? a : b;
}
 
int const N = 100005;
 
ll mx[N << 2], lz[N << 2];
int ql, qr;
 
ll suma[N], sumb[N], pos[N], f[N];
 
inline void pushdown(int k) {
    mx[k << 1] += lz[k];
    mx[k << 1 | 1] += lz[k];
    lz[k << 1] += lz[k];
    lz[k << 1 | 1] += lz[k];
    lz[k] = 0;
}
 
inline void pushup(int k) {
    mx[k] = max(mx[k << 1], mx[k << 1 | 1]);
}
 
ll query(int k, int l, int r) {
    if (ql > qr)
        return 0;
    if (ql <= l && r <= qr)
        return mx[k];
    if (lz[k])
        pushdown(k);
    int mid = (l + r) >> 1;
    ll ret = 0;
    if (ql <= mid)
        ret = max(ret, query(k << 1, l, mid));
    if (qr > mid)
        ret = max(ret, query(k << 1 | 1, mid + 1, r));
    pushup(k);
    return ret;
}
 
void add(int k, int l, int r, ll val) {
    if (ql <= l && r <= qr) {
        mx[k] += val;
        lz[k] += val;
        return;
    }
    if (lz[k])
        pushdown(k);
    int mid = (l + r) >> 1;
    if (ql <= mid)
        add(k << 1, l, mid, val);
    if (qr > mid)
        add(k << 1 | 1, mid + 1, r, val);
    pushup(k);
}
 
void motifiy(int k, int l, int r, int posy, ll val) {
    if (l == r) {
        mx[k] = max(mx[k], val);
        return;
    }
    if (lz[k])
        pushdown(k);
    int mid = (l + r) >> 1;
    if (posy <= mid)
        motifiy(k << 1, l, mid, posy, val);
    else
        motifiy(k << 1 | 1, mid + 1, r, posy, val);
    pushup(k);
}
 
struct point {
    int x, y;
    int a, b;
    bool operator < (point const& oth) const {
        if (x == oth.x)
            return y < oth.y;
        return x < oth.x;
    }
};
 
int main() {
    point v;
    int n;
    while (cin >> n) {
        auto mp = map<int, vector<point>>();
        auto det = vector<int>();
        det.push_back(0);
        for (int i = 0; i < n; ++i) {
            cin >> v.x >> v.y >> v.a >> v.b;
            mp[v.x].emplace_back(v);
            det.push_back(v.y);
        }
        sort(det.begin(), det.end());
        det.erase(unique(det.begin(), det.end()), det.end());
        for (int i = 0; i <= SZ(det) * 4; ++i)
            mx[i] = lz[i] = 0;
        for (auto& b : mp) {
            auto& tp = b.second;
            sort(tp.begin(), tp.end());
            for (int i = 0; i < SZ(tp); ++i) {
                suma[i] = tp[i].a + (i ? suma[i - 1] : 0);
                sumb[i] = tp[i].b + (i ? sumb[i - 1] : 0);
                pos[i] = lower_bound(all(det), tp[i].y) - det.begin() + 1;
            }
            for (int i = 0; i < SZ(tp); ++i) {
                ql = 1, qr = pos[i];
                f[i] = query(1, 1, SZ(det)) + sumb[i] + suma[SZ(tp) - 1] - suma[i];
            }
            ql = 1, qr = pos[0] - 1;
            add(1, 1, SZ(det), suma[SZ(tp) - 1]);
            for (int i = 0; i < SZ(tp); ++i) {
                ql = pos[i], qr = (i == SZ(tp) - 1) ? SZ(det) : (pos[i + 1] - 1);
                add(1, 1, SZ(det), sumb[i] + suma[SZ(tp) - 1] - suma[i]);
            }
            for (int i = 0; i < SZ(tp); ++i)
                motifiy(1, 1, SZ(det), pos[i], f[i]);
            /////DEBUG
            // for (int i = 1; i <= SZ(det); ++i) {
            //     ql = i, qr = i;
            //     cerr << query(1, 1, SZ(det)) << ' ';
            // }
            // cerr << '\n';
        }
        ql = 1, qr = SZ(det);
        cout << query(1, 1, SZ(det)) << '\n';
    }
}

J

傻逼题

def main():
    try:
        while True:
            x, a, y, b = map(int, input().split(' '))
            t1, t2 = x * b, a * y
            if t1 == t2:
                print('=')
            elif t1 < t2:
                print('<')
            else:
                print('>')
    except EOFError as e:
        pass
 
main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值