首页 > 其他分享 >2024.4.21 模拟赛

2024.4.21 模拟赛

时间:2024-04-24 19:55:50浏览次数:16  
标签:2024.4 21 int ll ++ ans -- 模拟 define

A perm

首先若 \((i+j)\) 为奇数则需要满足其中一个是奇数,另一个必须是偶数。

若 \(k=0\),那么要求 \(A_i\) 和 \(A_j\) 同号,也就是所有数必须都是同一奇偶性。若满足则答案为 \(n!\) ,否则为 \(0\) 。

若 \(k=1\) ,那么要求 \(A_i\) 和 \(A_j\) 异号。奇下标位置为 \(\lceil \frac{n}{2}\rceil=x\) 个,偶下标位置为 \(\lfloor\frac{n}{2}\rfloor=y\) 个。若 \(a_i\) 中奇数数量和偶数数量分别为 \(x、y\) 或者 \(y、x\) 那么就可以成功放进去。而同奇偶性的数顺序无关,所以方案数为 \(x!y!\) ,否则方案数为 \(0\) 。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
#define V vector<long long int>
#define pb push_back
#define pi pair<long long int, long long int>
#define forl(var, str, end) for (long long int var = str; var < end; var++)

ll mod = 147744151;

int main() {
    freopen("perm.in", "r", stdin);
    freopen("perm.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    ll t;
    cin >> t;
    ll N = 100001;
    ll fact[N];
    fact[0] = 1;
    fact[1] = 1;
    for (ll i = 2; i < N; i++) {
        fact[i] = (i * fact[i - 1]) % mod;
        fact[i] %= mod;
    }
    while (t--) {
        ll n, k;
        cin >> n >> k;
        ll odd = 0;
        ll eve = 0;
        ll x;
        forl(i, 0, n) {
            cin >> x;
            if (x % 2)
                odd++;
            else
                eve++;
        }
        if (n == 1) {
            cout << "1\n";
            continue;
        }
        if (k == 0 && (odd == 0 || eve == 0)) {
            cout << fact[n] << "\n";
            continue;
        }
        if (k == 0 || (k == 1 && (odd == 0 || eve == 0)))
            cout << "0\n";
        else {
            if (abs(odd - eve) > 1)
                cout << "0\n";
            else {
                ll ans = (odd == eve) ? 2 : 1;
                ans = (((ans * fact[eve]) % mod) * fact[odd]) % mod;
                cout << ans << "\n";
            }
        }
    }
    return 0;
}

B seg

分析一下美丽区间内的数应该长什么样。不妨设这个区间内的数构成数组 \(B\),假设 \(B_1\leq B_2\leq...\leq B_k\),那么限制等价于对于所有 \(2\leq i\leq k\) 满足 \(B_i\) 是 \(B_{i-1}\) 的倍数。

所以我们只需要选取一些元素加进这个数组 \(B\) ,也就是把它们放在一起,其余的元素排列顺序无所谓。而且每次要么不选,要么把这种数字全部选完。

因为贡献的计算和最小值有关,所以设 \(dp(x)\) 表示当前加入数组 \(B\) 里最小值是 \(x\) 时,\(B\) 数组大小为多少。

转移有 \(dp(x)=\max(dp(i·x))+cnt(x)\) ,然后取 \(dp(x)\times x\) 的最大值即为答案。

时间复杂度 \(\mathcal O(n+A_{max}\log A_{max})\)。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll INF = 1e18;
const string YES = "YES";
const string NO = "NO";

void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    map<int, vector<int>> factors;
    unordered_map<int, int> dp;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        int x = a[i];
        if (factors.find(x) == factors.end()) {
            for (int d = 1; d * d <= x; d++) {
                if (x % d == 0) {
                    factors[x].push_back(d);
                    if ((x / d) != d) {
                        factors[x].push_back(x / d);
                    }
                }
            }
            sort(factors[x].begin(), factors[x].end());
        }
    }

    ll ans = 0;
    sort(a.rbegin(), a.rend());

    for (int i = 0; i < n; i++) {
        vector<int> f = factors[a[i]];
        for (auto &d : f) {
            dp[d] = max(dp[d], 1 + dp[a[i]]);
        }
    }

    for (int i = 0; i < n; i++) {
        ans = max(ans, 1LL * dp[a[i]] * a[i]);
    }

    cout << ans << endl;
}

int main() {
    freopen("seg.in", "r", stdin);
    freopen("seg.out", "w", stdout);
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

C destroy

摧毁最多的道路就对应着保留最少的路径。当 \(s_1=s_2,t_1=t_2\) 时,直接保留最短路即可。

所以答案上界是 \(dis(s_1,t_1)+dis(s_2,t_2)\),其中 \(dis(x,y)\) 表示 \(x\) 到 \(y\) 的最短路长度。

但是存在两条路径会重叠的情况,重叠情况只需要被计算一次。

由于 \(n,m\) 只有 \(3000\) ,所以可以通过枚举两个最短路重叠部分。设枚举的重叠部分端点为 \(a,b\) ,那么答案为 \(\min_{(a,b)}\{dis(a,b)+\min(dis(s_1,a)+dis(b,t_1),dis(t_1,a)+dis(b,s_1))+\min(这里和前一个式子类似)\}\).

所以需要处理任意两点最短路,使用 BFS 即可。时间复杂度 \(\mathcal O(n(n+m))\)。

#include <cstdio>
#define For(a, b, c) for (register int a = b; a <= c; ++a)
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define CMin(a, b) (a > (b) ? a = (b) : 0)
int s1, s2, t1, t2, l1, l2, N, M, d[3005][3005], to[6010], n[6010], l[3005], num, ans;
inline void Add(int x, int y) {
    to[++num] = x;
    n[num] = l[y];
    l[y] = num;
    to[++num] = y;
    n[num] = l[x];
    l[x] = num;
}
void Bfs(int x) {
    int q[6002] = { x }, t = 0, h = 0, now, *dis = d[x];
    bool vis[3005] = {};
    vis[x] = 1;
    while (h <= t, now = q[h++])
        for (int zd, zb = l[now]; zb; zb = n[zb]) {
            zd = to[zb];
            if (vis[zd])
                continue;
            dis[zd] = dis[now] + 1;
            vis[zd] = 1;
            q[++t] = zd;
        }
}
int main() {
    freopen("destroy.in", "r", stdin);
    freopen("destroy.out", "w", stdout);
    scanf("%d%d", &N, &M);
    For(a, 1, M) {
        scanf("%d%d", &t1, &t2);
        Add(t1, t2);
    }
    For(a, 1, N) Bfs(a);
    scanf("%d%d%d%d%d%d", &s1, &t1, &l1, &s2, &t2, &l2);

    if (d[s1][t1] > l1 || d[s2][t2] > l2)
        return !printf("-1");
    ans = d[s1][t1] + d[s2][t2];

    int md, da, db, dc, dd, *d1 = d[s1], *d2 = d[t1], *d3 = d[s2], *d4 = d[t2];
    For(i, 1, N) For(j, i + 1, N) {
        md = d[i][j];
        da = Min(d1[i], d1[j]);
        db = Min(d2[i], d2[j]);
        dc = Min(d3[i], d3[j]);
        dd = Min(d4[i], d4[j]);
        if (da + db + md > l1 || dc + dd + md > l2)
            continue;
        CMin(ans, da + db + dc + dd + md);
    }
    return !printf("%d", M - ans);
}

D party

因为 \(2\) 操作是对整个连通块做贡献,所以使用并查集维护比较方便,记 \(cnt_i\) 为以 \(i\) 为代表节点的并查集举行了多少次聚会。

现在考虑 \(1\) 操作,操作相当于将两个并查集合并。假设将 \(Y\) 合并到 \(X\) 上,会发现此时 \(X\) 此前的聚会并未对 \(Y\) 进行贡献。要对 \(Y\) 的代表节点 \(y\) 再记一个 \(tmp_y\),表示父亲节点在与自己合并之前,已经举行了多少次聚会,来起到一个差分的作用。

另外多出 \(1\) 点关系度的两人,可以用哈希表来记录。考虑查询,如果不在一个并查集里关系度肯定为 \(0\)。反之,则先暴力跳到他们的 \(lca\),再暴力地往上跳,每次先加 \(cnt\) 再减 \(tmp\)。

最后的答案,还要先减去 \(lca\) 两个儿子 \(tmp\) 的较大值,再加上哈希表里二人额外的关系度。因为并查集树高是 \(log\) 级别的,所以暴力往上跳的复杂度是正确的。时间复杂度 \(O(n logn)\)

#include <cstdio>
#include <cstring>
#include <map>
#define reg register
#define max(_a, _b) (_a > _b ? _a : _b)
const int N = 100006;
int fa[N], siz[N], r[N], pas[N], a[30], b[30];
std::map<int, int> un[N];
int R() {
    reg int x = 0;
    reg char ch = getchar_unlocked();
    reg bool f = 0;
    for (; ch < 48 || ch > 57; ch = getchar_unlocked()) f |= ch == '-';
    for (; ch >= 48 && ch <= 57; ch = getchar_unlocked()) x = x * 10 + ch - 48;
    return f ? -x : x;
}
int find(reg int x) {
    while (x != fa[x]) x = fa[x];
    return x;
}
void swap(reg int &a, reg int &b) {
    reg int t = a;
    a = b, b = t;
}
void unionset(reg int u, reg int v) {
    reg int x = find(u), y = find(v);
    if (x == y)
        return;
    if (siz[x] < siz[y])
        swap(x, y);
    fa[y] = x, siz[x] += siz[y], pas[y] = r[x];
}
int main() {
    freopen("party.in", "r", stdin);
    freopen("party.out", "w", stdout);
    reg int n = R(), m = R(), t1, t2, ans, opt, x, y, i, j;
    for (i = 1; i <= n; ++i) fa[i] = i, siz[i] = 1;
    while (m--) {
        opt = R(), x = R(), y;
        if (opt == 1) {
            y = R();
            unionset(x, y);
            if (x > y)
                swap(x, y);
            ++un[x][y];
        } else if (opt == 2)
            ++r[find(x)];
        else {
            y = R(), t1 = t2 = ans = 0;
            if (find(x) != find(y)) {
                puts("0");
                continue;
            }
            for (i = x; i != fa[i]; i = fa[i]) a[++t1] = i;
            a[++t1] = i;
            for (i = y; i != fa[i]; i = fa[i]) b[++t2] = i;
            b[++t2] = i;
            for (i = t1, j = t2; a[i] == b[j]; --i, --j) ans += r[a[i]] - pas[a[i]];
            ans -= max(pas[a[i]], pas[b[j]]);
            if (x > y)
                swap(x, y);
            if (un[x].find(y) != un[x].end())
                ans += un[x][y];
            printf("%d\n", ans);
        }
    }
}

E graph

考虑 \(L=R\) ,此时图一定是基环森林。那么只有 \(T\) 为 \(S\) 祖先或者 \(T\) 在环上的时候答案不为 \(-1\) ,直接把图建出来做就行。

对于其他情况,当 \(T\) 不在环上时,问题可以转化为:

找到最小的 \(k\) 使得有 \(k\) 个值域在 \([L,R]\) 中的数和为 \(a\) 。

这个直接预处理每个 \(a\) 的答案即可。

当 \(T\) 在环上的时,问题可以转化为:

找到最小的 \(k\) 使得有 \(k\) 个值域在 \([L,R]\) 中的数和为 \(ai+b\) ,其中 \(a\) 是环长。

答案是连通块大小级别。因为 \(k\) 个数能组合出 \([kL,kR]\) 间的所有数,当 \(kR-kL\geq a\) 的时候一定能覆盖所有同余系。

当 \(b\geq a\) 时,把限制变为 \(ci+b\%a\) 其中限制 \(c\geq \lfloor\frac{b}{a}\rfloor\),然后把 \([kL,kR]\) 区间按照 \(k\) 从大到小排序,维护 \(c\geq x\) 时候每个同余系的最小的 \(k\) 。具体实现可能需要提前把区间相交处理一下。时间复杂度 \(\mathcal O(n\log n)\)。

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define app push_back
#define all(x) (x).begin(), (x).end()
#ifdef LOCAL
#define debug(...) [](auto... a) { ((cout << a << ' '), ...) << endl; }(#__VA_ARGS__, ":", __VA_ARGS__)
#else
#define debug(...)
#endif
#ifdef LOCAL
#define __int128 long long
#endif  // LOCAL
const int inf = 1e18;
const int maxn = 2e5 + 5;
int a[maxn];
int col[maxn];
bool used[maxn];
int h[maxn];
int de[maxn];
int u[maxn][20];
bool cy[maxn];
int di[maxn];
int me[maxn];
int mo[maxn];
int f[maxn];
vector<int> ps;
vector<int> mem[maxn];
int32_t main() {
    freopen("graph.in", "r", stdin);
    freopen("graph.out", "w", stdout);
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, l, r;
    cin >> n >> l >> r;
    set<int> unu;
    f[0] = 0;
    fill(f + 1, f + maxn, inf);
    for (int i = 1; i <= n; ++i) unu.insert(i);
    queue<int> q;
    q.push(0);
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        set<int>::iterator it = unu.lower_bound(x + l);
        while (it != unu.end() && (*it) <= x + r) {
            f[*it] = f[x] + 1;
            q.push(*it);
            unu.erase(it);
            it = unu.lower_bound(x + l);
        }
    }
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        u[i][0] = a[i];
    }
    for (int j = 1; j < 20; ++j) {
        for (int i = 1; i <= n; ++i) {
            u[i][j] = u[u[i][j - 1]][j - 1];
        }
    }
    for (int i = 1; i <= n; ++i) {
        cy[u[i][19]] = true;
    }
    for (int i = 1; i <= n; ++i) {
        if (cy[i] && !used[i]) {
            int x = i;
            int c = 0;
            while (true) {
                col[x] = i;
                used[x] = true;
                h[x] = c;
                ++c;
                x = a[x];
                if (x == i)
                    break;
            }
            de[i] = c;
            ps.app(de[i]);
        }
    }
    for (int i = 1; i <= n; ++i) {
        if (cy[i]) {
            me[i] = i;
            continue;
        }
        di[i] = 0;
        int x = i;
        for (int j = 19; j >= 0; --j) {
            if (!cy[u[x][j]]) {
                di[i] += (1 << j);
                x = u[x][j];
            }
        }
        me[i] = a[x];
        di[i]++;
        mo[de[col[me[i]]]] = max(mo[de[col[me[i]]]], di[i]);
    }
    sort(all(ps));
    ps.erase(unique(all(ps)), ps.end());
    auto ok = [&](int p, int x) -> bool { return ((l + 2 * p - 1 - x) / p != (r + 2 * p - x) / p); };
    for (int p : ps) {
        vector<int> ans(mo[p] + p + 1);
        fill(all(ans), inf);
        ans[0] = 0;
        set<int> unuse;
        for (int i = 1; i < ans.size(); ++i) {
            unuse.insert(i);
            unuse.insert(i + p);
        }
        queue<int> q;
        q.push(0);
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            int wis = x + l;
            for (int i = 20; i >= 0; --i) {
                if (wis - p * (1 << i) >= ((int)(ans.size())))
                    wis -= p * (1 << i);
            }
            if (wis >= ans.size())
                wis -= p;
            debug(x, wis);
            set<int>::iterator it = unuse.lower_bound(wis);
            while (it != unuse.end() && (*it) <= x + r && ok(p, (*it) - x)) {
                int v = (*it);
                unuse.erase(it);
                if (v >= ans.size()) {
                    v -= p;
                }
                if (ans[v] == inf) {
                    ans[v] = ans[x] + 1;
                    q.push(v);
                }
                if (unuse.count(v))
                    unuse.erase(v);
                it = unuse.lower_bound(wis);
            }
        }
        for (int i = ans.size() - 1 - p; i >= 0; --i) {
            ans[i] = min(ans[i], ans[i + p]);
        }
        debug(ans.size());
        for (int x : ans) debug(x);
        mem[p] = ans;
    }
    auto up = [&](int x, int h) {
        for (int i = 19; i >= 0; --i) {
            if (h & (1 << i)) {
                x = u[x][i];
            }
        }
        return x;
    };
    int q1;
    cin >> q1;
    while (q1--) {
        int x, y;
        cin >> x >> y;
        if (col[me[x]] != col[me[y]]) {
            cout << (-1) << '\n';
            continue;
        }
        if (!cy[y]) {
            if (di[x] < di[y]) {
                cout << (-1) << '\n';
                continue;
            }
            if (x == y) {
                cout << 0 << '\n';
                continue;
            }
            int h = di[x] - di[y];
            if (up(x, h) != y) {
                cout << (-1) << '\n';
                continue;
            }
            cout << (f[h] == inf ? -1 : f[h]) << '\n';
            continue;
        }
        int p = de[col[me[x]]];
        int o = ((h[y] - h[me[x]]) % p + p) % p;
        o += di[x];
        if (o < mem[p].size()) {
            cout << (mem[p][o] == inf ? -1 : mem[p][o]) << '\n';
        } else {
            for (int i = 20; i >= 0; --i) {
                if (o - p * (1 << i) >= ((int)(mem[p].size()))) {
                    o -= p * (1 << i);
                }
            }
            o -= p;
            cout << (mem[p][o] == inf ? -1 : mem[p][o]) << '\n';
        }
    }
    return 0;
}
/*
2 101 101
2 1
1
1 2
*/

标签:2024.4,21,int,ll,++,ans,--,模拟,define
From: https://www.cnblogs.com/xhqdmmz/p/18156199

相关文章

  • 「洛谷」题解:P1217 回文质数
    题目传送门看着题目好像很简单的样子,实际上做起来才会发现,这么多函数他奶奶的是普及-难度?在这道题目当中,我们最少需要写两个函数,如果需要优化可以再多写一个,待会儿的代码我们就直接放最简单版本的了。有人说这道题可以暴力对拍之后再输出,这完全可以,但是这么简单的题目不至于使用......
  • Win10_x64 21h2调试体系分析
    参考https://www.52pojie.cn/thread-1728894-1-1.html记的笔记,发出来参考下,抛砖引玉,有错误多纠正。还望各位大佬别嘲笑。平台如下:版本      Windows10专业版版本号      21H2安装日期      &#8206;2021-&#8206;08-&#8206;23操作系统内部版本  ......
  • AP5121是一款外围电路简单的多功能平均电流型LED 恒流驱动器
    AP5121是一款外围电路简单的多功能平均电流型LED恒流驱动器,适用于宽电压范围的非隔离式大功率恒流LED驱动领域。芯片PWM端口支持超小占空比的PWM调光,可响应最小60ns脉宽。芯片采用我司专利算法,为客户提供最佳解决方案,最大限度地发挥灯具优势,以实现景观舞台灯高辉的调......
  • JTCR-网络-21
    InetAddressInetAddress类用于封装IP地址或者域名,支持IPv4和IPv6。创建InetAddress对象需要使用工厂方法,因为没有提供显式构造器。工厂方法如下staticInetAddressgetLocalhost();staticInetAddressgetByName(StringhostName);//一个域名对应多个IP地址时,返回......
  • 第21章 控制器和视图(一)
    1准备工作添加包:dotnetaddpackageMicrosoft.AspNetCore.Mvc.Razor.RuntimeCompilation--version3.1.12开始使用视图2.1配置应用程序HTML响应是使用视图创建的,视图则是混合了HTML元素和C#表达式的文件。配置Startup来启用HTML响应。services.AddControllersWithVie......
  • 2024.4.23 近期练习
    CF1924D先考虑一个串的最长合法序列,维护一个栈,答案就是右括号加入时栈非空的次数。我们看成从\((0,0)\)走到\((n,m)\),发现没被匹配的右括号个数就是\(x-y\)的最大值。要想只有\(k\)个匹配,那么要和\(x-y=m-k\)“相切”。若\(f(k)\)表示穿过直线的方案数,答案是\(f(k......
  • CVE-2021-34371 Neo4j-Shell 漏洞复现
    前言偶然的一次机会遇到了这个漏洞,决定在vulhub复现下,重要提醒:本次复现所需要的环境为java8kali更换java环境戳这里漏洞描述Neo4j到3.4.18(启用shell服务器)公开了一个RMI服务,该服务可以任意反序列化Java对象,例如通过setSessionVariable。攻击者可滥用此漏洞进行远程......
  • day21-阶段总结
    1.知识点补充1.1并发编程&网络编程从知识点的角度来看,本身两者其实没有什么关系:网络编程,基于网络基础知识、socket模块实现网络的数据传输。并发编程,基于多进程、多线程等来提升程序的执行效率。但是,在很多“框架”的内部其实会让两者结合起来,使用多进程、多线......
  • GJOI 2024.4.20 总结
    Morning:T1小鸟Statement:在一个\(n\)的二维平面里,\(X\)轴的正方向是向右的,\(Y\)轴的正方向是向上的。在坐标系第一象限里,左下角的点的坐标是\((0,0)\),右上角的点的坐标是\((n-1,n-1)\)。所以本题我们考虑的整个平面总共有\(n\timesn\)个整点,每个整点都有一只小......
  • Virtuoso绘制模拟模块Frame并导出LEF
    数模混合Flow时一些pin多的模拟模块可以通过导出lib和LEF,合并到数字flow中进行自动布线。第一步肯定是和后端那边确定macro的形状以及各个端口的出pin方向和metallayer。这些确认完了之后,就可以开始做lef了。网络上的教程交的是用abstract做,但实际上这是个很老旧的软件了,现在vi......