首页 > 编程语言 >2024“钉耙编程”中国大学生算法设计超级联赛(4)

2024“钉耙编程”中国大学生算法设计超级联赛(4)

时间:2024-07-29 19:17:43浏览次数:14  
标签:钉耙 std const val int 编程 2024 type id

Preface

最唐氏的一集,有人写 03 一直过不去红温了然后白兰了一整场,怎么回事呢

最后很可惜 06 因为多维数组调用时顺序出了点问题,导致 cache 爆了然后常数太大 TLE 了,但凡时间延长 1min 都改完过了

由于今天过的题少就只写过了的六个题,剩下时间还要写昨晚 CF 的博客


最优 K 子段

致敬传奇红温闪总赛时挂了 17 发还没过,最后扔给徐神看一眼就秒了

首先一眼二分答案 \(lim\),然后考虑贪心地找满足要求的子段

枚举当前的右端点 \(r\),则满足要求的左端点 \(l\) 需要满足 \(pfx_r-pfx_{l-1}\ge lim\),同时要满足 \(r-l+1\) 为质数

考虑按照 \(pfx_{l-1}\) 的大小从小到大枚举所有左端点并依次检验,由于数据随机且质数的分布大致均匀,可以发现如果合法的左端点则不会找太多次就能找到

复杂度约为 \(O(n\log^2 n)\)

#include <bits/stdc++.h>

bool is_prime[1000001];

int n, k;
int a[1000001];

int check(int limit) {
    std::set<std::pair<int, int>> s{ {0, 0} };
    int res = 0;

    for(int i = 1; i <= n; ++i) {
        auto it = s.begin();
        while(it != s.end()) {
            if(a[i] - it->first < limit) it = s.end();
            else if(is_prime[i - it->second]) break;
            else it++;
        }
        if(it != s.end()) res += 1, s.clear();
        s.insert({a[i], i});
    }

    return res;
}

void work() {
    std::cin >> n >> k;
    for(int i = 1; i <= n; ++i) std::cin >> a[i], a[i] += a[i - 1];
    if(k + k > n) {
        std::cout << "impossible\n";
        return ;
    }
    int L = -2000, R = 10000000, M;
    while(L < R) {
        M = (L + R + 1) / 2;
        if(check(M) >= k) L = M;
        else R = M - 1;
    }
    std::cout << L << char(10);
}

int main() {
    std::ios::sync_with_stdio(false);
    memset(is_prime, 1, sizeof(is_prime));
    is_prime[0] = is_prime[1] = 0;
    for(int64_t i = 2; i <= 1000000; ++i) if(is_prime[i])
        for(int64_t j = i * i; j <= 1000000; j += i) is_prime[j] = false;
    int T; std::cin >> T; while(T--) work();
    return 0;
}


多层血条

签到,只需特别处理要替换为 . 的位置即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,m,hp,dmg; char s[805];
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        RI i,j; scanf("%d%d%d%d",&n,&m,&hp,&dmg);
        int k=hp/m; for (i=1;i<=m;++i) s[i]=' ';
        if (k>0)
        {
            for (i=1;i<=m;++i) s[i]=(k-1)%5+'A';
        }
        int rem=hp-m*k;
        for (i=1;i<=rem;++i) s[i]=(k+1-1)%5+'A';
        if (dmg>=m)
        {
            for (i=1;i<=m;++i) s[i]='.';
        } else
        {
            for (i=rem;i>=1&&dmg>0;--i) s[i]='.',--dmg;
            for (i=m;i>rem&&dmg>0;--i) s[i]='.',--dmg;
        }
        putchar('+'); for (i=1;i<=m;++i) putchar('-'); puts("+");
        for (i=1;i<=n;++i)
        {
            putchar('|'); for (j=1;j<=m;++j) putchar(s[j]); puts("|");
        }
        putchar('+'); for (i=1;i<=m;++i) putchar('-'); puts("+");
    }
    return 0;
}

延时操控

赛时基本已经过了这题,但被卡 cache 了,最后改了数组顺序然后没交上去

首先有一个很关键的转化,可以看作两个人先一起走若干步,然后再让己方角色多走 \(k\) 步

因此可以考虑一个暴力 DP,需要记录的是当前的轮次、己方角色的坐标、对方角色的坐标,以及对方的血量

但直接这样搞复杂度是 \(O(n^4m\times hp)\) 的,还有个每次转移选一个方向的常数,铁定是过不去的

考虑优化,注意到如果第二个人不撞墙,则两人位置之差将保持不变;而一次撞墙会导致某一维的差值产生 \(\pm 1\) 的变化

因此实质上对方角色的坐标只要存因为撞墙导致的差值即可,复杂度变为 \(O(n^2m\times hp^3)\),可以通过

#include<bits/stdc++.h>
using namespace std;
const int MOD = 1e9+7;
void add(int &x, int a){if ((x+=a)>=MOD) x-=MOD;}

using pii = pair<int, int>;

int n, m, k, hp;
string tbl[52];
int f[51][52][52][11][11][6];
int g[51][52][52];
const int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

void solve(){
    cin >> n >> m >> k >> hp;
    tbl[0] = tbl[n+1] = string(n+2, '#');
    for (int i=1; i<=n; ++i){
        cin >> tbl[i];
        tbl[i] = "#" + tbl[i] + "#";
    }
    int px, py, ex, ey;
    for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j){
        if (tbl[i][j]=='P') px=i, py=j;
        if (tbl[i][j]=='E') ex=i, ey=j;
    }
    int difx = ex-px, dify = ey-py;

    for (int w=0; w<=m; ++w) for (int x=1; x<=n; ++x) for (int y=1; y<=n; ++y){
        if (tbl[x][y]=='#') g[w][x][y] = 0;
        else g[w][x][y] = (0==w ? 1 : 0);
        for (int ddx=0; ddx<=10; ++ddx) for (int ddy=0; ddy<=10; ++ddy){
            for (int h=0; h<=hp; ++h) f[w][x][y][ddx][ddy][h]=0;
        }
    }
    f[0][px][py][5][5][hp]=1;


    for (int w=0; w<m-k; ++w) for (int d=0; d<4; ++d){
        int dx=dir[d][0], dy=dir[d][1];
        for (int x=1; x<=n; ++x) for (int y=1; y<=n; ++y){
            if (tbl[x+dx][y+dy]=='#') continue;
            for (int ddx=0; ddx<=10; ++ddx) for (int ddy=0; ddy<=10; ++ddy){
                int eex=x+difx+ddx-5, eey=y+dify+ddy-5;
                if (eex<=0 || eex>n || eey<=0 || eey>n) continue;
                int nx=eex+dx, ny=eey+dy;
                for (int h=hp; h>0; --h){
                    if (0==f[w][x][y][ddx][ddy][h]) continue;
                    if (tbl[nx][ny]=='#') add(f[w+1][x+dx][y+dy][ddx-dx][ddy-dy][h-1], f[w][x][y][ddx][ddy][h]);
                    else add(f[w+1][x+dx][y+dy][ddx][ddy][h], f[w][x][y][ddx][ddy][h]);
                }
            }
        }
    }

    for (int w=0; w<k; ++w){
        for (int x=1; x<=n; ++x) for (int y=1; y<=n; ++y){
            if (!g[w][x][y]) continue;
            if (tbl[x][y]=='#') continue;
                for (int d=0; d<4; ++d){
                    int nx=x+dir[d][0], ny=y+dir[d][1];
                    if ('#'!=tbl[nx][ny]) add(g[w+1][nx][ny], g[w][x][y]);
                }
            }
    }

    int ans=0;
    for (int w=0; w<=m-k; ++w) for (int x=1; x<=n; ++x) for (int y=1; y<=n; ++y){
        for (int ddx=0; ddx<=10; ++ddx) for (int ddy=0; ddy<=10; ++ddy){
            add(ans, 1LL*f[w][x][y][ddx][ddy][0]*g[k][x][y]%MOD);
        }
    }
    cout << ans << '\n';
}


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

序列更新

这场好多随机数据题,不过这题还是比较显然的

考虑设一个阈值 \(S\),对于某个 \(a_i\),若 \(\{b_i\}\) 中大于 \(a_i\) 的数的个数 \(\le S\),则可以将这个 \(a_i\) 放入将会改变当前这个数的 vector 中,这部分的复杂度就是 \(O(n\times S)\)

对于剩下的数我们可以每次暴力更新,直到它们符合上面讲的限制

由于数据随机,某个数经历一次暴力更新操作仍然不符合限制的概率为 \(1-\frac{S}{n}\),因此做一个等比数列求和得到这部分复杂度为 \(O(\frac{n^2}{S})\)

因此取 \(S=\sqrt n\) 即可得到 \(O(n\sqrt n)\) 的算法

#include<cstdio>
#include<iostream>
#include<utility>
#include<algorithm>
#include<vector>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=250005,LIM=500;
int t,n,q,a[N],b[N],rk[N]; pi p[N]; vector <int> pos[N];
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        RI i,j; long long sum=0; scanf("%d%d",&n,&q);
        for (i=0;i<n;++i) scanf("%d",&a[i]),sum+=a[i];
        for (i=0;i<n;++i) scanf("%d",&b[i]),p[i]=pi(b[i],i);
        for (sort(p,p+n),i=0;i<n;++i) rk[p[i].se]=i;
        for (i=0;i<n;++i) pos[i].clear(); vector <int> vec;
        for (i=0;i<n;++i)
        {
            int mp=upper_bound(p,p+n,pi(a[i],n))-p;
            if (n-mp<=LIM)
            {
                for (j=mp;j<n;++j) pos[(p[j].se-i+n)%n].push_back(i);
            } else vec.push_back(i);
        }
        /*for (i=0;i<n;++i)
        {
            printf("pos = %d : ",i);
            for (auto x:pos[i]) printf("%d ",x);
            putchar('\n');
        }*/
        while (q--)
        {
            int k; scanf("%d",&k);
            for (auto x:pos[k]) sum-=a[x],a[x]=max(a[x],b[(x+k)%n]),sum+=a[x];
            pos[k].clear(); vector <int> tmp;
            for (auto x:vec)
            {
                if (b[(x+k)%n]>a[x])
                {
                    sum-=a[x]; a[x]=b[(x+k)%n]; sum+=a[x];
                    int mp=rk[(x+k)%n]+1;
                    if (n-mp<=LIM)
                    {
                        for (i=mp;i<n;++i) pos[(p[i].se-x+n)%n].push_back(x);
                    } else tmp.push_back(x);
                } else tmp.push_back(x);
            }
            vec=tmp; printf("%lld\n",sum);
            //for (i=0;i<n;++i) printf("%d%c",a[i]," \n"[i==n-1]);
        }
    }
    return 0;
}

昵称检索

众所周知字符串一般我题都不看就扔给徐神

#include <bits/stdc++.h>

constexpr int $n = 1000006;

std::vector<std::string> date;

int go[$n][26], dp1[$n], dp2[$n];

void work() {
    int n, m; std::cin >> n >> m;
    std::string s; std::cin >> s;
    s = "a" + s + "0";
    static int g[26];
    memset(g, -1, sizeof(int) * 10);
    for(int i = 1; i <= m + 1; ++i) if(std::isdigit(s[i])) {
        memcpy(go[i], g, sizeof(int) * 10);
        g[s[i] - '0'] = i;
    }
    memset(g, -1, sizeof(g));
    for(int i = m; i >= 0; --i) if(std::isalpha(s[i])) {
        memcpy(go[i], g, sizeof(g));
        g[s[i] - 'a'] = i;
    }
    memset(dp1, 0, sizeof(int) * (m + 2));
    memset(dp2, 0, sizeof(int) * (m + 2));
    while(n--) {
        static char name[25]; std::cin >> name;
        int cur = 0;
        for(int i = 0; cur >= 0 && name[i]; ++i) cur = go[cur][name[i] - 'a'];
        if(cur >= 0) dp1[cur] += 1;
    }
    for(auto date: date) {
        int cur = m + 1;
        for(int i = 0; cur >= 0 && i < date.size(); ++i) cur = go[cur][date[i] - '0'];
        if(cur >= 0) dp2[cur] += 1;
    }
    // for(int i = 1; i <= m; ++i) std::cout << dp1[i] << ' ' << dp2[i] << char(10);
    // for(int i = 2; i <= m; ++i) dp1[i] += dp1[i - 1];
    for(int i = m - 1; i; --i) dp2[i] += dp2[i + 1];
    int64_t ans = 0;
    for(int i = 1; i < m; ++i) ans += int64_t(dp1[i]) * int64_t(dp2[i + 1]);
    std::cout << ans << char(10);
    return;
}

int main() {
    std::ios::sync_with_stdio(false);
    {
        constexpr int day[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        for(int i = 0; i < 12; ++i) for(int j = 0; j < day[i]; ++j) {
            static char s[10];
            sprintf(s, "%02d%02d", i + 1, j + 1);
            std::reverse(s, s + 4);
            date.push_back(std::string(s));
        }
    }
    int T; std::cin >> T; while(T--) work();
    return 0;
}

寻找宝藏

首先考虑没有限制时,通过 DP 预处理一些东西

令 \(f_i\) 表示从 \((0,0)\) 到 \((i,p_i)\) 的最大收益,\(g_i\) 表示从 \((i,p_i)\) 到 \((n+1,n+1)\) 的最大收益,这个很容易用扫描线预先求出

对于询问可以分类讨论合并两边的贡献,可以离线后用四个树状数组维护,总复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>

struct Rect {
    int x1, y1, x2, y2;
    int64_t val[2], ans;
};

struct Event {
    int type, x, y, id;
    int64_t val;
};

struct FWT {
    std::vector<int64_t> c;

    FWT(size_t n): c(n + 1, 0) {}

    void reset() {
        c.assign(c.size(), 0);
    }

    void update(size_t i, int64_t val) {
        i += 1;
        assert(i < c.size());
        while(i < c.size()) {
            if(val > c[i]) c[i] = val;
            i += i&-i;
        }
    }

    int64_t get(size_t i) const {
        i += 1;
        assert(i < c.size());
        int64_t res = 0;
        while(i > 0) {
            res = std::max(res, c[i]);
            i ^= i&-i;
        }
        return res;
    }
};

void work() {
    int n, m; std::cin >> n >> m;
    std::vector<int> p(n + 2);
    std::vector<int64_t> v(n + 2), ans(n + 2, 0);
    p[0] = v[0] = 0; for(int i = 1; i <= n; ++i) std::cin >> p[i] >> v[i]; p[n + 1] = n + 1, v[n + 1] = 0;
    
    std::vector<Rect> rect(m);
    for(auto &[x1, y1, x2, y2, val, ans]: rect) {
        std::cin >> x1 >> y1 >> x2 >> y2;
        val[0] = val[1] = ans = 0;
    }
    
    std::vector<Event> event;
    for(int i = 0; i <= n + 1; ++i) event.emplace_back(Event {
        .type = 0,
        .x = i,
        .y = p[i],
        .id = i,
        .val = v[i],
    });

    for(int i = 0; i < m; ++i) {
        auto [x1, y1, x2, y2, val, ans] = rect[i];
        event.emplace_back(Event {
            .type = 1,
            .x = x1 - 1,
            .y = y2 + 1,
            .id = i,
            .val = 0,
        });
        event.emplace_back(Event {
            .type = 2,
            .x = x2 + 1,
            .y = y1 - 1,
            .id = i,
            .val = 0,
        });
    }

    auto get_infer = [&](int type, int id) -> int64_t& {
        if(type) return rect[id].val[type - 1];
        else     return ans[id];
    };

    std::sort(event.begin(), event.end(), [&](const Event &a, const Event &b) {
        return a.x != b.x ? a.x < b.x
             : a.y != b.y ? a.y < b.y
             : a.type < b.type;
    });
    FWT c(n + 2);
    for(auto [type, x, y, id, val]: event) {
        const int64_t hkr = c.get(y) + val;
        get_infer(type, id) += hkr;
        c.update(y, hkr);
    }

    std::sort(event.begin(), event.end(), [&](const Event &a, const Event &b) {
        return a.x != b.x ? a.x > b.x
             : a.y != b.y ? a.y > b.y
             : a.type > b.type;
    });
    c.reset();
    for(auto [type, x, y, id, val]: event) {
        const int64_t hkr = c.get(n + 1 - y) + val;
        get_infer(type, id) += hkr;
        c.update(n + 1 - y, hkr);
    }

    for(int i = 1; i <= n; ++i) ans[i] -= v[i];

    std::sort(event.begin(), event.end(), [&](const Event &a, const Event &b) {
        return a.x != b.x ? a.x < b.x
             : a.y != b.y ? a.y > b.y
             : a.type < b.type;
    });
    c.reset();
    for(auto [type, x, y, id, val]: event) {
        c.update(n + 1 - y, get_infer(type, id));
        if(type == 1) rect[id].ans = std::max(rect[id].ans, c.get(n + 1 - y));
    }

    std::sort(event.begin(), event.end(), [&](const Event &a, const Event &b) {
        return a.x != b.x ? a.x > b.x
             : a.y != b.y ? a.y < b.y
             : a.type < b.type;
    });
    c.reset();
    for(auto [type, x, y, id, val]: event) {
        c.update(y, get_infer(type, id));
        if(type == 2) rect[id].ans = std::max(rect[id].ans, c.get(y));
    }

    for(int i = 0; i < m; ++i) std::cout << rect[i].ans << char(10);
}

int main() {
    std::ios::sync_with_stdio(false);
    int T; std::cin >> T; while(T--) work();
    return 0;
}

Postscript

感觉这场策略有大问题,被 03 搞红温了就几乎全程零作用了,本来换一下我去接手队友的题可能会更好

标签:钉耙,std,const,val,int,编程,2024,type,id
From: https://www.cnblogs.com/cjjsb/p/18330851

相关文章

  • 2024最新梦想贩卖机,变现宝知识付费小程序(修改版本+前后端)
    梦想贩卖机升级版,变现宝吸取了资源变现类产品的很多优点,摒弃了那些无关紧要的东西,使本产品在运营和变现能力上,实现了质的超越。多领域素材资源知识变现营销裂变独立版。实现流量互导,多渠道变现。独立部署,可绑自有独立域名不限制域网盘下载链接:https://blog.zibovip.top/?p=1......
  • Adobe2024全家桶免费安装包下载路径+方法教程
    Adobe发布了其全家桶的最新版本Adobe2024。Adobe全家桶是一组由AdobeSystems开发和发行的图形设计、影像编辑与网络开发的软件产品套装,包括图像编辑软件Photoshop、矢量图形设计软件Illustrator等多款知名软件。Adobe全家桶的更新不仅意味着新功能的增加和性能的提升,也预示着......
  • Adobe2024全家桶下载+详细安装教程
    “我电脑里安装了20多个Adobe软件,但真正用到的只有PS。”近日,有网友在社交平台发帖称,自己的电脑里安装了大量Adobe软件,但实际上只经常使用Photoshop。对此,有其他网友回复道:“你这是买椟还珠,Adobe全家桶里有很多宝藏工具,比如AE、PR、AU等。”Adobe全家桶永久免费领取入口:http......
  • 华为OD笔试机试 - 园区参观路径 (Java 2024年C卷D卷真题算法)
    华为OD机试(C卷+D卷)2024真题目录(Java&c++&python)题目描述园区某部门举办了FamilyDay,邀请员工及其家属参加;将公司园区视为一个矩形,起始园区设置在左上角,终点园区设置在右下角;家属参观园区时,只能向右和向下园区前进,求从起始园区到终点园区会有多少条不同的参观路径......
  • 华为OD笔试机试真题算法 - 密码解密 (Java 2024年C卷D卷)
    华为OD机试(C卷+D卷)2024真题目录(Java&c++&python)题目描述给定一段“密文”字符串s,其中字符都是经过“密码本”映射的,现需要将“密文”解密并输出。映射的规则(‘a’~‘i’)分别用(‘1’~‘9’)表示;(‘j’~‘z’)分别用(“10*”~“26*”)表示。约束:映射始终唯一。......
  • 【学术会议征稿】2024年第七届机械工程与智能制造国际会议(WCMEIM 2024)
    2024年第七届机械工程与智能制造国际会议(WCMEIM2024)20247th WorldConferenceonMechanicalEngineeringandIntelligentManufacturing   WCMEIM会议属一年一度的国际学术盛会。因其影响力及重要性,WCMEIM会议自创建筹办以来,便受到国内外高等院校、科学研究所和企事......
  • 【学术会议征稿】第九届计算机技术与机械电气工程国际学术论坛(ISCME 2024)
    第九届计算机技术与机械电气工程国际学术论坛(ISCME2024)20249th InternationalSeminaronComputerTechnology,MechanicalandElectricalEngineering第九届计算机技术与机械电气工程国际学术论坛(ISCME2024)将于2024年11月8-10日在中国南京隆重召开。本次论坛将围绕“......
  • C++ 【元编程】检查类型是否具有成员 hasattr
    在python中,可以使用hasattr判断类型是否具有某个成员。在C++中,有的时候我们要写一个模板函数,需要对模板进行一定的限制时。这些限制可能为“该模板函数仅用于拥有某个成员的类型”。在标准<type_traits>中,规定了一些列如is_copy_assignable等模板常量,用于判断是否拥有拷贝构造......
  • 【本届确定SPIE出版-稳定EI检索—往届均已成功见刊检索】第四届检测技术与自动化工程
    第四届检测技术与自动化工程国际学术会议(TTAE2024)将拟定于2024年9月6-8日中国厦门召开。检测技术与自动化工程国际学术会议将每年举行一次,旨在将“检测技术”和“自动化工程”等学术领域的学者、专家、研发者、技术人员聚集到一个学术交流的平台,并且提供一个共享科研成果、......
  • 【IEEE-CPS独立出版,高录用,该出版社检索快速且稳定!收稿主题大,管理、计算机相关主题皆可
    2024年创新与信息管理国际会议(ICIIM2024)为第四届管理科学和软件工程国际学术会议(ICMSSE2024)的分会,主会由ACM珠海分会,广州番禺职业技术学院主办;全国区块链行业产教融合共同体承办,将于2024年9月6-8日于广州召开。会议旨在为从事管理与信息工程领域的专家学者、工程技术人员、......