首页 > 其他分享 >NOIP联测25

NOIP联测25

时间:2022-11-11 19:00:09浏览次数:50  
标签:25 return NOIP int rep 联测 MEX dp define

T1.将军棋

其实挺水的一道题,但是出题人丧心病狂强迫分类讨论,故意增加码量。
\(A\) 挨着查直到队伍不同
\(B、C\) 记录每种颜色最后出现的位置,枚举是哪一种颜色。对于 \(B\) 枚举两个,如果都不是,那就是第三个;对于 \(C\) 手动二分。
\(D\)、100 先判断是否为新颜色,若不是再二分。
二分显然就是查 \([mid, i - 1]\) 和 \([mid, i]\) 队伍数量是否相同,相同说明 \(i\) 这个队伍在 \([mid, i - 1]\) 中出现过,找到最后一个这样的位置。注意到前 \(i-1\) 的颜色我们已知,所以根本不需要查询,直接暴力找出来即可。

代码
#define sandom signed
#include "generals.h"
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
const int Z = 1010;

int n, m, k = 1;
int dp[Z][Z];
bool vs[Z];
vector<int> ans;
inline int ask(int l, int r)
{
    if (dp[l][r]) return dp[l][r];
    return dp[l][r] = query(l, r);
}
inline int count(int l, int r)
{
    int cnt = 0; memset(vs, 0, sizeof(vs));
    rep(i, l, r) if (!vs[ans[i - 1]]) cnt++, vs[ans[i - 1]] = 1;
    return cnt;
}
inline bool check(int j, int i) { return count(j, i - 1) == ask(j, i); }
inline int getcol(int i)
{
    int l = 1, r = i - 1, res = 0;
    while (l <= r)
    {
        int mid = l + r >> 1;
        if (check(mid, i)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    return res - 1;
}
int pos[10];
inline bool cmp(int x, int y) { return x > y; }
vector<int> getColor(int T, int n, int Q)
{
    ans.push_back(++m); pos[k = 1] = 1;
    if (T <= 15)//队伍连续
    {
        rep(i, 2, n)
        {
            if (ask(k, i) == 1) ans.push_back(m);
            else k = i, ans.push_back(++m);
        }
    }
    else if (T <= 34)//最多3个队伍
    {
        rep(i, 2, n) 
        {
            sort(pos + 1, pos + 5, cmp);
            if (pos[1] && check(pos[1], i)) ans.push_back(ans[pos[1] - 1]), pos[1] = i;
            else if (pos[2] && check(pos[2], i)) ans.push_back(ans[pos[2] - 1]), pos[2] = i;
            else if (pos[3]) ans.push_back(ans[pos[3] - 1]), pos[3] = i;
            else ans.push_back(++m), pos[3] = i;//新颜色
        }
    }
    else if (T <= 48)//最多4个队伍
    {
        rep(i, 2, n) 
        {
            sort(pos + 1, pos + 5, cmp);
            if (!pos[2] && check(pos[1], i)) ans.push_back(ans[pos[1] - 1]), pos[1] = i;
            else if (pos[2] && check(pos[2], i))
            {
                if (pos[1] && check(pos[1], i)) ans.push_back(ans[pos[1] - 1]), pos[1] = i;
                else ans.push_back(ans[pos[2] - 1]), pos[2] = i;
            }
            else if (pos[3] && check(pos[3], i)) ans.push_back(ans[pos[3] - 1]), pos[3] = i;
            else if (pos[4]) ans.push_back(ans[pos[4] - 1]), pos[4] = i;
            else ans.push_back(++m), pos[4] = i;//新颜色
        }
    }
    else//二分查询
    {
        rep(i, 2, n)
        {
            if (ask(1, i) == ask(1, i - 1) + 1) ans.push_back(++m);
            else ans.push_back(ans[getcol(i)]);
        }
    }
    return ans;
}

T2.小明的变换

考场推了个假结论,然而是一道模拟题。
操作其实就是把相同的数放在后面,尝试匹配 \(a、b\) 的每一项,如果能匹配直接匹配,如果不能开个桶记录 \(a\) 中前面出现过的数,当后面有相同的需要时,从前面的桶中直接拿。全部匹配成功就是有解。

代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <iostream>
#include <cstring>
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
using namespace std; typedef long long ll; typedef unsigned long long ull;
namespace IO
{
    const int bif = 1 << 18; char buf[bif], *p1, *p2;
    inline char getc() { if (p1 == p2) { p2 = (p1 = buf) + fread(buf, 1, bif, stdin); if (p1 == p2) return EOF; } return *p1++; }
    inline int read() { int x = 0, f = 0; char c = getc(); while (!isdigit(c)) f |= c == '-', c = getc(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getc(); return f ? -x : x; }
}; using namespace IO;
const int Z = 1e6 + 10;

int n, a[Z], b[Z], cnt[Z];
sandom main()
{
    fre(trans, trans);
    int T = read();
    while (T--)
    {
        memset(cnt, 0, sizeof(cnt));
        n = read(); 
        rep(i, 1, n) a[i] = read();
        rep(i, 1, n) b[i] = read();
        int i = 1, op = 1;
        rep(j, 1, n)
        {
            while (b[j] != a[i] && i <= n) cnt[a[i++]]++;//找到匹配项
            if (i > n) { op = 0; break; }//匹配不上
            if (cnt[a[i]]) cnt[a[i]]--;//如果前面有直接拿过来
            else i++;//否则判定为匹配对
        }
        if (op) puts("YES");
        else puts("NO");
    }
    return 0;
}

T3.小明过生日

题意:有一个长度为 \(n\) 的字符串,给定一个序列 \(a\),当满足前 \(a_1\) 个字符,接着 \(a_2\) 个字符,接着 \(a_3\) 个字符…… 为回文串时,且构造出的 \(b\) 同理时,字符串中所有字符都相同。
根据回文串,给已知相同的字符连边,转化为图论问题,构造出\(b\)使得\(n\)个点之间连通。
一个点在一个序列中最多连一条边,那么整张图最多 \(2n\) 条边,想要连通至少需要 \(2(n-1)\) 条边。发现每一个奇数段都有一个点没有连边,但是我们最多只能少 \(2\) 条边,所以当\(a\)中奇数段超过\(2\)时,无解。
根据回文串的性质:错位回文则全部字符相同。一个奇数段\(k\)与一个相差为1的偶数段\(k-1\),可以满足前\(k\)连通,并且\(k\)处恰好留出一个接口,连接下一条边。通过规律发现以下构造方法:把奇数段放在首尾两段。
\(b\)为\(a_1-1、a_2、a_3、a_m-1\),手磨一下不难发现是对的。

代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <iostream>
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
using namespace std; typedef long long ll; typedef unsigned long long ull;
namespace IO
{
    const int bif = 1 << 18; char buf[bif], *p1, *p2; int wrt[20], Tp = 0;
    inline char getc() { if (p1 == p2) { p2 = (p1 = buf) + fread(buf, 1, bif, stdin); if (p1 == p2) return EOF; } return *p1++; }
    inline int read() { int x = 0, f = 0; char c = getc(); while (!isdigit(c)) f |= c == '-', c = getc(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getc(); return f ? -x : x; }
    inline void write(int x) { if (x < 0) putchar('-'), x = -x; do { wrt[++Tp] = x % 10, x /= 10; } while (x); while (Tp) putchar(wrt[Tp--] | 48); putchar(' '); }
}; using namespace IO;
const int Z = 1e5 + 10;

int n, m, k;
int a[Z], b[Z];
sandom main()
{
    fre(birth, birth);
    n = read(), m = read();
    if (m == 1)
    {
        int x = read();
        write(x), putchar('\n');
        write(2), putchar('\n');
        write(x - 1), write(1), putchar('\n');
        return 0;
    }
    rep(i, 1, m) if ((a[i] = read()) & 1) k++;
    if (k > 2) { puts("-1"); return 0; }
    rep(i, 2, m) if (a[i] & 1)
    {
        if (a[1] & 1) swap(a[i], a[m]);
        else swap(a[i], a[1]);
    }
    rep(i, 1, m) write(a[i]); putchar('\n');
    rep(i, 1, m) b[i] = a[i];  b[1]--, b[m]++;
    write(m), putchar('\n');
    rep(i, 1, m) write(b[i]);
    return 0;
}

T4.小明爱数数

本来以为和数列挺像的,所以弄了四维\(dp\),发现复杂度严重不对,就没继续想了。
定义 \(dp[i][j][k]\) 表示考虑了前\(i\)个数,当前\(MEX\)为\(k\),已经有了\(j\)个不同的大于\(k\)的数。转移如下:

1.对\(MEX\)不产生影响:
选择小于\(MEX\)的:\(ddp[i][j][k] += dp[i-1][j][k] * k\)
选择大于\(MEX\)且出现过的:\(dp[i][j][k] += dp[i-1][j][k] * j\)
选择大于\(MEX\)且未出现过的,但是因为不确定具体是谁,所以此处不计算贡献,把贡献看作单位1:\(dp[i][j][k] += dp[i-1][j - 1][k]\)

2.对\(MEX\)产生影响:
显然就是选择了\(MEX\)。枚举直接的 \(MEX\) 是 \(t\),则就是 \(i\) 这个位置放了 \(t\) 使得 \(MEX\)由\(t\)变为\(k\),此时\(t->k\)中间的数一定都出现过,共有\(k-t-1\)个。因为在\(j\)处没有计算贡献,所以现在可以从\(j\)强制选这\(k-t-1\)个,剩下的还是不计算贡献,那么转移就是\(dp[i][j][k] += dp[i-1][j + k - t - 1][t] * A_{j + k - t - 1}^{k - t - 1}\)。

时间复杂度\(O(n^2k^2)\),对第二种转移用前缀和优化,令\(s=j+k-1\),那么累加的答案就是\(dp[s-t][t]*\frac{(s-t)!}{j!}\),令\(sum[x][y]=\sum\limits_{t=0}^{y}dp[x-t][t] * (x-t)!\),注意边界问题。
我一直以为是边界写错了,调了一个半小时,结果是tm数组越界了。

代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <iostream>
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define dwn(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std; typedef long long ll; typedef unsigned long long ull;
#define int long long 
namespace IO
{
    const int bif = 1 << 18; char buf[bif], *p1, *p2; 
    inline char getc() { if (p1 == p2) { p2 = (p1 = buf) + fread(buf, 1, bif, stdin); if (p1 == p2) return EOF; } return *p1++; }
    inline int read() { int x = 0, f = 0; char c = getc(); while (!isdigit(c)) f |= c == '-', c = getc(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getc(); return f ? -x : x; }
}; using namespace IO;
const int Z = 2100, mod = 998244353;
inline int max(int a, int b) { return a > b ? a : b; } inline int min(int a, int b) { return a < b ? a : b; }
inline int qpow(int a, int b, int c) { int res = 1; while (b) { if (b & 1) res = res * a % c; a = a * a % c; b >>= 1; } return res; }

int n, m, k, ans;
int l[Z], r[Z];
int fac[Z], inv[Z];
inline void init()
{
    fac[0] = 1;
    rep(i, 1, n) fac[i] = fac[i - 1] * i % mod;
    inv[n] = qpow(fac[n], mod - 2, mod);
    dwn(i, n - 1, 0) inv[i] = inv[i + 1] * (i + 1) % mod;
}
inline int A(int n, int m) { return n < m ? 0 : fac[n] * inv[n - m] % mod; }
int dp[2][Z][Z], sum[Z][Z];
//dp[i][j][k]:前i个数,MEX为k,有j个不同的数大于MEX
sandom main()
{
    fre(numb, numb);
    n = read(), k = read();
    init();
    rep(i, 1, n)
    {
        int b = read();
        l[i] = max(b - k, 0);
        r[i] = min(b + k, i);
    }
    dp[0][0][0] = 1;
    rep(i, 1, n)
    {
        int u = i & 1, v = i - 1 & 1;
        rep(j, max(l[i] - 1, 0), i - 1) rep(k, l[i - 1], min(r[i - 1], j)) sum[j][k] = ((k ? sum[j][k - 1] : 0) + dp[v][j - k][k] * fac[j - k]) % mod;
        rep(j, 0, i) rep(k, l[i], r[i])
        {
            if (j + k > i) break;//后面一定不合法
            dp[u][j][k] += dp[v][j][k] * k;//选择小于mex的
            dp[u][j][k] += dp[v][j][k] * j;//选择大于mex且出现过的
            if (j) dp[u][j][k] += dp[v][j - 1][k];//选择大于mex且未出现过的,但是不计算贡献
            // rep(t, l[i - 1], r[i - 1])
            //     if (k > t) dp[u][j][k] += dp[v][j + k - t - 1][t] * A(j + k - t - 1, k - t - 1);
            if (k) dp[u][j][k] += sum[j + k - 1][min(k - 1, r[i - 1])] * inv[j];//选择mex,利用前缀和优化
            dp[u][j][k] %= mod;
        }
        rep(j, 0, i - 1) rep(k, l[i - 1], r[i - 1]) dp[v][j][k] = sum[j][k] = 0;
    }
    rep(j, 0, n) rep(k, l[n], r[n]) (ans += dp[n & 1][j][k] * A(n - k, j)) %= mod;
    cout << (ans + mod) % mod;
    return 0;
}

标签:25,return,NOIP,int,rep,联测,MEX,dp,define
From: https://www.cnblogs.com/sandom/p/16881479.html

相关文章

  • 223201062522刘晋-软件工程基础Y- 实验二 结对项目报告
    沈阳航空航天大学软件工程基础实验报告实验名称:实验二实验题目:结对项目专业软件工程学号223201062522姓名刘晋结对伙伴赵德龙指导教师孟桂英......
  • 爬取豆瓣TOP250
    实验1基于多线程的静态网页爬取项目1.实验目的(1)熟悉网页浏览器开发工具的使用;(2)掌握网页爬取requests库的使用;(3)掌握网页解析技术,例如Xpath、BeautifulSoup、re等;(4)......
  • P2254 [NOI2005] 瑰丽华尔兹 题解
    注意到可以设朴素转移方程\(f_{t,i,j}\)表示\(t\)时刻钢琴在\((i,j)\)时的最长滑动距离,这样复杂度是\(O(nmt)\)的,过不去。不过听说加点奇怪的优化能过?考虑一段时......
  • Cinema 4D R2023.1(c4d r25 mac)
    Cinema4DR2023.1是Mac上知名的3D动画设计制作软件,包含GPU渲染器Prorender、生产级实时视窗着色、超强破碎、场景重建等诸多新功能,C4Dmac为用户提供高端的3D内容创建,......
  • P5017 NOIP2018 普及组 摆渡车
    P5017NOIP2018普及组摆渡车-洛谷|计算机科学教育新生态(luogu.com.cn)显然要把人按照到达时间排序。然后考虑dp。设\(f(i)\)表示前\(i\)个人已上车或到达目......
  • 2022 noip记(noip前)
    每日一记updcsp-s2结束后虽然csp-s2在SD取消了,但也自测了一下,感觉比去年简单了,各大oj都是290左右,T1T2都会T3T4暴力没打满了属于是qwq。(顺便做了一下普及组的题除了T2挂......
  • 25、递归搜索目录找出最大的文件
    题目:  在变量名serach_dir中,随意添加一个文件路径,找出所有文件下最大的文件。思路:  1、输入文件路径。  2、递归遍历该文件路径下所有子目录。  3、遍历子目......
  • 【安装文档】TRex流量分析仪保姆级安装指南--基于VMware虚拟机(ubantu18.04@Intel 8254
    前言既然你已经知道TRex并尝试搜索它的安装教程,这意味着你有一定的基础知识(至少知道自己需要什么)。因此本文对于TRex的介绍部分会偏少本次主要为TRex安装过程的一次记录(......
  • IT25589: LIST HISTORY COMMAND FAILS WITH DB21018E ERROR WHEN THE HISTORY FILE CO
    KnownIssues https://www.ibm.com/mysupport/s/defect/aCI3p000000kF7p/dt159458?language=en_USIT25589:LISTHISTORYCOMMANDFAILSWITHDB21018EERRORW......
  • 25-jmeter-使用简单数据写入器保存测试结果
    前言jmeter做性能压测的时候,我们希望把每次的结果保存下来,方便写测试总结报告。可以用的监听器SimpleDataWriter,保存测试的结果简单数据写入SimpleDataWriter添加-......