2019.10.22模拟赛

T1 合并集合

有\(n\)个集合,第\(i\)个集合记为\(S_i\),集合按环状排列,即第\(i + 1\)个集合右边是第\(i\)个集合,第\(n\)个集合右边是第\(i\)个集合。
一开始每个集合里只有一个数,每次你可以选择两个相邻的集合\(S,T\),然后合并成\(S \cup T\),之后你可以获得收益\(|S| \times |T|\),其中\(|S|\)表示集合\(S\)的元素个数。
你需要一直进行以上的操作直到只剩一个集合为止,求能获得的最大的收益之和。

断环为链,复制两倍区间DP。

signed main() {
    cin >> n;
    for (register int i = 1; i <= n; ++i) cin >> a[i];
    for (register int i = 1; i <= n; ++i) a[i + n] = a[i];
    for (register int len = 1; len <= n; ++len) {
        for (register int l = 1, r = len + l - 1; r <= 2 * n; ++l, ++r) {
            cnt.reset();
            for (register int i = l; i <= r; ++i) cnt[a[i]] = 1;
            sum[l][r] = cnt.count();
            //          cerr << l << " " << r << " " << sum[l][r] << endl;
        }
    }
    for (register int len = 2; len <= n; ++len) {
        for (register int l = 1, r = l + len - 1; r <= 2 * n; ++l, ++r) {
            for (register int k = l; k < r; ++k) {
                f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r] + sum[l][k] * sum[k + 1][r]);
            }
        }
    }
    int ans = 0;
    for (register int i = 1; i <= n; ++i) ans = max(ans, f[i][i + n - 1]);
    cerr << ans << endl;
    cout << ans << endl;
}

T2 爬

小 M 把小 D 困在了一个高度为\(L\)的陷阱里,小 D 有\(N\)天时间可以爬出来,他有\(N\)个药丸,每天吃一颗,顺序任意。
第 颗药丸可以让他早上爬\(A_i\),下午滑下去\(B_i\),如果他任何时候能爬到井口,就能爬出来。
小 M 每天晚上都会放水,第\(i\)天晚上水位会升高\(C_i\),如果小 D 不能在晚上保证严格高于水位,就会被淹死。
问小 D 最早第几天可以爬出来,如果无解输出\(-1\)。

贪心 + 二分答案。
直接二分多少天能爬出来。
check的时候贪心的取能往上走的最大值。
之后在前mid天中找下滑最少的或者在后边的天中找上爬最大的作为最后一天的药丸。

inline bool judge(const int &X) {
    if (sum[X] >= L)
        return 1;
    register int pos = 1;
    for (register int i = 1; i < X; ++i)
        while (sum[i + 1] - med[pos].y <= a[i]) ++pos;
    int mx = 0;
    for (register int i = pos; i <= X; ++i) mx = max(med[i].y, mx);
    if (sum[X] + mx >= L)
        return 1;
    mx = 0;
    for (register int i = X + 1; i <= n; ++i) mx = max(med[i].x, mx);
    if (sum[X - 1] + mx >= L)
        return 1;
    return false;
}
signed main() {
    cin >> n >> L;
    for (register int i = 1; i <= n; ++i) cin >> med[i].x >> med[i].y;
    for (register int i = 1; i <= n; ++i) cin >> a[i], a[i] += a[i - 1];
    sort(med + 1, med + 1 + n, cmp);
    int pos = 1;
    for (register int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + med[i].x - med[i].y;
    for (; pos <= n; ++pos)
        if (sum[pos] <= a[pos])
            break;
    if (pos > n)
        --pos;
    register int l = 1, r = pos, res = -1, mid;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (judge(mid))
            res = mid, r = mid - 1;
        else
            l = mid + 1;
    }
    cout << res << endl;
}

T3

小 M 和小 D 在玩游戏。
小 M 有个\(w\)行\(h\)列的棋盘, 每个格子都包含了至多一枚硬币,硬币正面朝上或反面朝上。
小 M 和小 D 轮流操作,小 M 先手。每个人可以选择棋盘中没在之前被选择过的一行或一列,然然后将所选的行或列上的硬币全部翻转,即正面变成反面,反面变成正面。
当所有硬币都正面朝上或者所有的行列都被选择,游戏结束。最后一次操作的玩家将会获得 分。如果当前局面所有的硬币都正面朝上,小 M 和小 D 都将获得\(2\)分的额外收益。
问两个人如果都按最大化自己分数的策略操作,那么小 M 最后的得分是多少。
保证每列至少有一个硬币,每行至少有一个反面朝上的硬币。

这道题还是一道蛮有意思的博弈。
因为最大化收益,所以能使所有的硬币朝上就尽量让所有的硬币朝上。
如果不行,每个人每次取一行或一列,所以如果\(h + w\)是奇数,先手必胜,否则后手必胜。
之后考虑所有硬币朝上。当前硬币如果朝上,那么它将会被翻偶数次,否则他将会被翻奇数次。因此我们可以对行与列连边进行二分图染色。
(菜鸡的我这是第一次写二分图 染色抄了半天)
之后每一个联通块,都会出现\(a_i\)个左部节点和\(b_i\)个右部节点,当\(a_i\)和\(b_i\)奇偶性不同会对结果造成影响,如果有奇数个这种情况,先手必胜。否则考虑奇偶性相同的操作的影响。

namespace ddd {
const int MAXN = 205;
int h, w;
struct node {
    int nxt, ver;
    bool edge;
} e[MAXN * MAXN];
int head[MAXN], tot;
inline void add(const int &x, const int &y, const bool &z) {
    e[++tot].ver = y;
    e[tot].nxt = head[x];
    e[tot].edge = z;
    head[x] = tot;
}
int v[MAXN];
unsigned char flag;
bool ans, ans1, ans2, cnt1, cnt2;
inline bool dfs(int x, int c) {
    v[x] = c;
    if (c == 1)
        cnt1 ^= 1;
    else
        cnt2 ^= 1;
    for (register int i = head[x]; i; i = e[i].nxt) {
        register int y = e[i].ver;
        if (!v[y]) {
            if (!dfs(y, e[i].edge ? 3 - c : c))
                return 0;
        } else if ((e[i].edge && v[x] == v[y]) || (!e[i].edge && v[x] != v[y]))
            return 0;
    }
    return 1;
}
inline void main() {
    memset(head, 0, sizeof(head));
    memset(v, 0, sizeof(v));
    tot = 0;
    ans1 = ans2 = 0;
    poread(h), poread(w);
    for (register int i = 1; i <= h; ++i) {
        for (register int j = 1; j <= w; ++j) {
            char c = charget();
            if (c == 'x')
                add(i, j + h, 1), add(j + h, i, 1);
            else if (c == 'o')
                add(i, j + h, 0), add(j + h, i, 0);
        }
    }
    for (register int i = 1; i <= h + w; ++i) {
        if (v[i])
            continue;
        cnt1 = cnt2 = 0;
        if (!dfs(i, 1)) {
            return (void)puts((h + w & 1) ? "1" : "0");
        }
        if (cnt1 != cnt2)
            ans1 ^= 1;
        else
            ans2 ^= cnt1;
    }
    return (void)puts(ans2 | ans1 ? "3" : "2");
}
}  // namespace ddd
signed main() {
    int T;
    poread(T);
    while (T--) ddd::main();
}

原文地址:https://www.cnblogs.com/Shiina-Rikka/p/11736153.html

时间: 2024-11-05 21:44:48

2019.10.22模拟赛的相关文章

10.22 模拟赛

10.22 模拟赛 T1 染色 考虑每个连通块删成一棵树就好了. mmp场上就我路径压缩写炸.... #include<iostream> #define MAXN 200006 using namespace std; int n , m; int fa[MAXN] , siz[MAXN] , book[MAXN] , sz[MAXN]; int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } int main() {

2019.10.24模拟赛赛后总结

本文原创,如果有不到位的地方欢迎通过右下角的按钮私信我! A.Icow Player 题目描述 被无止境的农活压榨得筋疲力尽后,Farmer John打算用他在MP3播放器市场新买的iCow来听些音乐,放松一下.FJ的iCow里存了N(1 <= N <= 1,000)首曲子,按1..N依次编号.至于曲子播放的顺序,则是按一个Farmer John自己设计的算法来决定: * 第i首曲子有一个初始权值R_i(1 <= R_i <= 10,000). * 当一首曲子播放完毕,接下来播放的

2019.10.18模拟赛T3

题目大意: 求$\sum\limits_{i=1}^n\sum\limits_{j=1}^n[lcm(i,j)>n](n\leq 10^{10})$的值. 题解: 这题貌似有n多种做法... 为了更好统计,把原式变为$n^2-\sum\limits_{i=1}^n\sum\limits_{j=1}^n[lcm(i,j)\leq n]$. 然后开始毒瘤... 首先,考虑枚举$lcm(i,j)$,设为$d$,计算有多少对$i.j$的最小公倍数为$d$. 设$i=p_1^{a_1}p_2^{a_2}\

2019.10.26模拟赛

T1 序列 给定一长度为\(n\)的序列\(s\),定义其健美值为:\[\sum\limits_{i=1}^{n}|s_i - i|\]因为 zzq 喜欢健美,所以 zzq 希望减小\(s\)的健美值,以衬托 zzq 的健美.为了达到 zzq 的目的,zzq 希望你对序列进行旋转操作,一次旋转操作可以使序列中的所有元素前移一位,并使\(s_1\)移动到\(s_n\). 可以进行任意次旋转操作,zzq 希望旋转后的健美值最小,请找出这个最小值. SOV 智商检测题 我们发现对于每个数,移动每一次会

2019.10.24模拟赛

T1 古代龙人的谜题 Mark Douglas 是一名调查员.他接受了「调查古代龙人」的任务.经过千辛万苦,Mark 终于找到了一位古代龙人.Mark 找到他时,他正在摆弄一些秘药,其中一些药丸由于是从很久以前流传下来的,发出了独特的光泽.古代龙人告诉了 Mark 一些他想知道的事情,看了看手中的秘药,决定考一考这位来访者. 古代龙人手中共有\(n\)粒秘药,我们可以用\(1\)表示「古老的秘药」,其余的用\(0\)表示.他将它们排成一列.古代龙人认为平衡是美的,于是他问 Mark 能选出多少个

2019.10.31模拟赛

说在前面 考场遇见原题??? 上午刚做的 下午就\(T3\)考到了\(2333\) 然而还是因为忘了取模挂了28分 T1 Dove下跳棋 数据太水输出n-1可得90 Dove 喜爱下跳棋,在传统的跳棋基础之上,Dove 又延伸出了许多别的玩法.Dove 以一个一维数轴为棋盘下跳棋,总共会移动棋子?? ? 1 次.因为讨厌没有规律,所以Dove 每次只会恰好把棋子向右移动?? 个格子. Cicada 送给了Dove 一个长度为?? 的数列{??},为了表示感谢,Dove 打算以Cicada 送给他

10.2模拟赛总结

10.2 模拟赛总结 T1. 数位dp: 一个非常非常非常非常显然的数位 DP \([L,R] = [1,R]-[1,L-1]\) 所以是分别求两次小于等于某个数字的方案数 \(f(i,j,k)\) 表示从低位数起的第 \(i\) 位,按照规则计算后答案为 \(j\quad (j=0,1)\) \(k\) 表示只考虑后面结尾和 \(lmt\)后面几位 的大小关系 \((k=0,1)\) 考虑第 \(i+1\) 位,算一下新构成的数字并判断下大小就可以了 注意到 \(L,R\) 数据范围特别大,需

10.31 模拟赛

10.31 模拟赛 A LIS 考虑每个数字前从 $ m $ 降序构造到 $ a_i $ 即可. #include <iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<vector> using namespace std; #define MAXN 300006 int n , m , k; int A[MAXN]; vector<int&g

10.5模拟赛

这么多模拟赛都没整理,能整理一天算一天吧qaq T1题面 sol:应该不难吧,分别对横坐标和纵坐标取差的绝对值,易知:如果互质就可以看到,否则就不行.然后出题人很毒瘤要用unsigned long long. #include <cstdio> #include <algorithm> using namespace std; long long x1,y1,x2,y2,c1=0,c2=0; unsigned long long x,y; unsigned long long AB