The 3rd Universal Cup. Stage 21: Ōokayama

news/2025/3/6 19:03:46/文章来源:https://www.cnblogs.com/cjjsb/p/18756135

Preface

神秘地抽到了一个毒瘤场,只能说以后真得避雷霓虹的比赛啊,纯纯的牢底坐穿

打完想补题发现没有英文 Tutorial,甚至官方的日语 Tutorial 里还有一些题是没的,索性直接开摆


B. Self Checkout

因为发病了一直再考虑一种不存在的虚空情况,后面写到一半发现原来是不存在的,直接人傻了

首先 \(1\) 只能出现在末尾且只能有一个;考虑剩下部分的序列一定是一个由 \(2,3\) 连续段交错的

对于一段连续的 \(k\)\(3\),不难发现其合法方案数是一个坐标系上走路的经典问题,推一下方案数是 \(C_{2k}^k-C_{2k}^{k-2}\)

对于非末尾的连续的 \(2\),显然方案数唯一(即原本由一段连续的 \(1\) 构成),且简单分析后可以发现这些 \(2\) 将前后连续的 \(3\) 段隔断,使得每部分的方案数独立

而对于末尾的 \(2\),可能存在将其中若干对 \(1\) 改为 \(2\) 的情况,本来以为会导致前面的一段 \(3\) 方案数变化的,但后面仔细思考发现只有全 \(2\) 的一种情况有影响,讨论下贡献即可

#include<cstdio>
#include<iostream>
#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=2e6+5,mod=998244353;
int n,s[N],fact[N],ifac[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{fact[0]=1; for (RI i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;ifac[n]=quick_pow(fact[n]); for (RI i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{if (n<0||m<0||n<m) return 0;return 1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod;
}
inline int calc(CI a,CI b)
{return (C(a+b,a)-C(a+b,b-2)+mod)%mod;
}
int main()
{scanf("%d",&n); init(2*n);for (RI i=1;i<=n;++i) scanf("%d",&s[i]);if (n==1) return printf("%d",s[1]==1?1:2),0;int cnt1=0;for (RI i=1;i<=n;++i) cnt1+=(s[i]==1);if (cnt1>=2) return puts("0"),0;if (cnt1==1&&s[n]!=1) return puts("0"),0;vector <pi> vec; vec.push_back(pi(s[1],1));for (RI i=2;i<=n;++i){if (s[i]==1) continue;if (s[i]==vec.back().fi) ++vec.back().se;else vec.push_back(pi(s[i],1));}int ans=1;for (auto [num,cnt]:vec){if (num==2) continue;ans=1LL*ans*calc(cnt,cnt)%mod;}if (cnt1==1) return printf("%d",ans),0;if (vec.back().fi==2){int pre3=vec.size()>1?vec[vec.size()-2].se:0;int coef=1LL*ans*quick_pow(calc(pre3,pre3))%mod;ans=(1LL*coef*calc(pre3+vec.back().se,pre3)%mod+1LL*ans*vec.back().se%mod)%mod;}return printf("%d",ans),0;
}

C. Segment Tree

考虑先用类似线段树的方式维护出每条长边连接的两点间实际的最短路,这个很容易支持修改

在询问的时候,考虑从底向上枚举经过哪一层走过去的,为了方便实现可以转化为两个点同时走直到碰面

具体地,我们可以维护一个四元组 \((l,r,dl,dr)\) 表示当前点能走到的区间为 \([l,r]\),到两个端点的距离分别为 \(dl,dr\),适时合并贡献即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1<<20;
int n,q,M,c[N],mn[N];
int main()
{scanf("%d",&n); M=(1<<n+1)-1;for (RI i=1;i<=M;++i) scanf("%d",&c[i]),mn[i]=c[i];for (RI i=M>>1;i>=1;--i) mn[i]=min(mn[i],mn[i<<1]+mn[i<<1|1]);// for (RI i=1;i<=M;++i) printf("%d%c",mn[i]," \n"[i==M]);for (scanf("%d",&q);q;--q){int opt,x,y; scanf("%d%d%d",&opt,&x,&y);if (opt==1){c[x]=y; if (x>(M>>1)) mn[x]=c[x],x>>=1;for (;x>0;x>>=1) mn[x]=min(c[x],mn[x<<1]+mn[x<<1|1]);} else{x+=(1<<n); y+=(1<<n)-1;int ans=x==y?c[x]:1e9;static int dlx[20],drx[20],dly[20],dry[20];dlx[n]=0; drx[n]=c[x]; dly[n]=c[y]; dry[n]=0;for (RI i=n-1;i>=0;--i){auto upt=[&](CI p,int* dl,int* dr){if (p&1){dl[i]=min(dl[i+1]+mn[p^1],dr[i+1]+c[p>>1]);dr[i]=min(dr[i+1],dl[i+1]+mn[p^1]+c[p>>1]);} else{dl[i]=min(dl[i+1],dr[i+1]+mn[p^1]+c[p>>1]);dr[i]=min(dr[i+1]+mn[p^1],dl[i+1]+c[p>>1]);}};upt(x,dlx,drx); upt(y,dly,dry);if (x!=y&&(x>>1)==(y>>1)) ans=min(ans,drx[i+1]+dly[i+1]);x>>=1; y>>=1;if (x==y) ans=min(ans,min(dlx[i]+dly[i],drx[i]+dry[i]));}printf("%d\n",ans);}}return 0;
}

E. ReTravel

首先不难观察到最后一定存在一种最优的路径,使得经过的点都在 \(n\) 个点构成的网格图上,即至少存在一个点与之横/纵坐标相同

由数据范围想到区间 DP,令 \(f_{l,r}\) 表示从起点走完区间 \([l,r]\) 中的所有点需要的最小贡献,显然 \(f_{i,i}=x_i+y_i\)

考虑两个点的情况,\(f_{k,k+1}=\max(x_k,x_{k+1})+\max(y_k,y_{k+1})=x_k+y_k+x_{k+1}+y_{k+1}-\min(x_k,x_{k+1})-\min(y_k,y_{k+1})\),即我们的策略是先走到 \((\min(x_k,x_{k+1}),\min(y_k,y_{k+1}))\) 这个点再分别走到两个点

那么画一下多个点的情况其实是等价的,对于一段区间 \([l,r]\) 我们就把它的参照点看作在 \((\min(x_k),\min(y_k)),k\in [l,r]\) 的一个虚拟点即可,这样把区间归纳为点后用经典的区间 DP 找最优分割位置即可

#include<cstdio>
#include<iostream>
#include<cstring>
#define RI register int
#define CI const int&
#define int long long
using namespace std;
const int N=505;
int n,x[N],y[N],f[N][N];
inline int DP(CI l,CI r)
{if (l==r) return x[l]+y[l];if (~f[l][r]) return f[l][r];int ret=1e18; for (RI k=l;k<r;++k)ret=min(ret,DP(l,k)+DP(k+1,r));int mnx=x[r],mny=y[r];for (RI i=l;i<r;++i) mnx=min(mnx,x[i]),mny=min(mny,y[i]);return f[l][r]=ret-(mnx+mny);
}
signed main()
{scanf("%lld",&n); memset(f,-1,sizeof(f));for (RI i=1;i<=n;++i) scanf("%lld%lld",&x[i],&y[i]);return printf("%lld",DP(1,n)),0;
}

L. Long Sequence Inversion 2

最战犯的一集,开局扔了基本已经会了的 B 后跟榜做 L,结果坐牢了半天发现不会,后面扔给徐神就直接秒了,我直接躺好了

#include <bits/stdc++.h>using llsi = long long signed int;constexpr llsi mod = 998244353;struct fwt {size_t n;std::vector<int> c;fwt(size_t n): c(n + 1, 0) { this->n = n; }void add(int p) {p += 1;for(int i = p; i <= n; i += i&-i) c[i] += 1;}llsi sum(int p) {p += 1;llsi res = 0;for(int i = p; i >  0; i -= i&-i) res += c[i];return res;}
};llsi inversion_calc(const std::vector<int> &p) {fwt c(p.size());llsi res = 0;for(int i = 0; i < p.size(); ++i) {res += i - c.sum(p[i]);c.add(p[i]);}return res % mod;
}constexpr llsi ksm(llsi a, llsi b) {llsi c = 1;while(b) {if(b & 1) c = c * a % mod;a = a * a % mod;b >>= 1;}return c;
}constexpr llsi inv2 = ksm(2, mod - 2);llsi l, b;
std::vector<int> p, ip;
std::vector<std::vector<int>> v;int main() {std::cin >> l >> b;p.resize(l); ip.resize(l);for(auto &p: p) std::cin >> p;for(int i = 0; i < l; ++i) ip[p[i]] = i;v.assign(l, std::vector<int>(b));for(auto &v: v) for(auto &v: v) std::cin >> v;// llsi ans = inversion_calc(v[ip[0]]);llsi ans = 0;fwt c(l);for(int i = 0; i < l; ++i) {ans = ans * b % mod;llsi group = c.sum(ip[i]);llsi copy = i - c.sum(ip[i]);group = ksm(b, group);copy = ksm(b, copy);// std::cerr << "group = " << group << char(10);// std::cerr << "copy = " << copy << char(10);// std::cerr << "inv(v[" << ip[i] << "]) = " << inversion_calc(v[ip[i]]) << char(10);c.add(ip[i]);ans +=group * group % mod *((copy * inversion_calc(v[ip[i]]) % mod) % mod +(inv2 * inv2 % mod * copy % mod * (copy - 1) % mod * b % mod * (b - 1) % mod)) % mod;ans %= mod;}std::cout << ans << char(10);return 0;
}

M. Cartesian Trees

赛后没有题解看过的队的代码发现好像是个弱智题啊,不过比赛的时候也没时间想了

考虑先用单调栈求出每个位置 \(i\) 向左向右第一个小于它的位置 \(L_i,R_i\)

对于两段区间,如果这两段区间在 \(i-L_i\) 数组和 \(R_i-i\) 数组上的合法的对应部分完全相同的话,则构成的两棵树同构

所谓的合法的对应部分,即要满足对应的端点在给出的区间内,即对于一组 \((L_i,i)\) 来说,只有满足 \(l\le L_i\le i\le r\) 的询问它才需要产生贡献

维护子数组一致性可以用 Hash,最后用扫描线+树状数组离线处理即可

#include<cstdio>
#include<iostream>
#include<vector>
#include<set>
#define RI register int
#define CI const int&
using namespace std;
typedef unsigned long long u64;
const u64 seed1=233,seed2=163;
const int N=400005;
int n,q,a[N],L[N],R[N],stk[N],top,x,y; u64 pw1[N],pw2[N];
class Tree_Array
{private:u64 bit[N];public:#define lowbit(x) (x&-x)inline void add(RI x,const u64& y){for (;x<=n;x+=lowbit(x)) bit[x]+=y;}inline u64 get(RI x,u64 ret=0){for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;}#undef lowbit
}BIT1,BIT2;
int main()
{scanf("%d",&n);for (RI i=1;i<=n;++i) scanf("%d",&a[i]);top=0; for (RI i=1;i<=n;++i){while (top&&a[stk[top]]>a[i]) --top;L[i]=stk[top]; stk[++top]=i;}top=0; for (RI i=n;i>=1;--i){while (top&&a[stk[top]]>a[i]) --top;R[i]=stk[top]; stk[++top]=i;}
//	for (RI i=1;i<=n;++i) printf("%d%c",L[i]," \n"[i==n]);
//	for (RI i=1;i<=n;++i) printf("%d%c",R[i]," \n"[i==n]);pw1[0]=pw2[0]=1ull;for (RI i=1;i<=n;++i) pw1[i]=pw1[i-1]*seed1,pw2[i]=pw2[i-1]*seed2;vector <pair <int,u64>> mdy1[N],mdy2[N];for (RI i=1;i<=n;++i){if (L[i]) mdy1[i].push_back(make_pair(L[i],pw1[i]*(i-L[i])));if (R[i]) mdy2[R[i]].push_back(make_pair(i,pw2[i]*(R[i]-i)));}scanf("%d",&q);vector <int> ques[N];for (RI i=1;i<=q;++i)scanf("%d%d",&x,&y),ques[y].push_back(x);set <pair <u64,u64>> st;for (RI i=1;i<=n;++i){for (auto [p,v]:mdy1[i]) BIT1.add(p,v);for (auto [p,v]:mdy2[i]) BIT2.add(p,v);for (auto j:ques[i]){u64 tmp1=(BIT1.get(i)-BIT1.get(j-1))*pw1[n-j];u64 tmp2=(BIT2.get(i)-BIT2.get(j-1))*pw2[n-j];st.insert(make_pair(tmp1,tmp2));}}return printf("%d",(int)st.size()),0;
}

Postscript

剩下的 A 题赛事感觉队友一直在做,到最后有个 \(O(n^2 \log n)\) 的神秘做法,不知道有没有写过

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/894760.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis与其使用方法讲解

ORM在讲解Mybatis之前,我们需了解一个概念ORM(Object-Relational Mapping)对象关系映射,其是数据库与Java对象进行映射的一个技术.通过使用ORM,我们可以不用编写负责的Sql语句,而是通过操作对象来实现增删改查操作缺优分析优点提高开发效率,减少代码的重复性和维护成本 增加代码…

系统流程图

1.图书馆借阅管理系统流程图: 背景说明:在学校图书馆借阅管理系统中,学生借阅图书需要经过一系列流程。首先,学生携带校园卡前往借阅处,工作人员通过刷卡设备读取学生信息,系统验证学生身份是否有效。若身份无效,系统提示原因(如校园卡过期、欠费等)。若身份有效,学生…

基于Microsoft.Extensions.AI核心库实现RAG应用

本文介绍了如何基于Microsoft.Extensions.AI + Microsoft.Extensions.VectorData 一步一步地实现一个RAG(检索增强生成)应用,相信会对你有所帮助。如果你也是.NET程序员希望参与AI应用的开发,那就快快了解和使用基于Microsoft.Extensioins.AI + Microsoft.Extensions.Vecto…

Linux shell su command All In One

Linux shell su command All In One su !== sudo substitute user => su, 替换用户/切换用户 superuser do => sudo, 执行超级管理员用户权限 su - run a command with substitute user and group ID su - 使用替代用户和组 ID 来运行命令Linux shell su command All In …

座椅控制器SCM

智能座椅控制器SCM是经纬恒润设计研发的车身舒适性控制模块,实现电动座椅14向位姿调节及通风、加热、腰托、按摩等控制功能。其基于人机交互和整车中央控制,匹配多种应用场景,实现迎宾、一键零重力、二排方便上下车等座舱内前后排座椅联动的姿态和空间自动调整。 智能…

React18 函数组件的底层渲染机制与形参props的使用细节

前言: Vue中的组件开发: 1.全局组件和局部组件 2.函数组件和类组件(vue3不具备functional函数组件) React中的组件化开发: 没有明确全局和局部的概念,可以理解为都是局部组件,不过可以把组件注册到React上,这样每个组件只要导入React中即可使用 1.函数组件 2.类组件 3.…

c# 托管堆和垃圾回收的clr的控制

前言 简单讲述一下垃圾回收,我们能做的一些控制。 正文 强制回收 class Program {static void Main(){var str = new StringBuilder();var x = "";for (int i = 0; i < 500; i++){x += "xxxxxxxxsadasdasdsadsaewqeqczxcxzgsfaswqeqwrqwewqeasdasqweqwrqsda…

PHP调用redis消息队列案例

一、代码实例://需要成功引入并使用redis$redis = RedisManager::$redis;// 生产者:将订单加入队列(右端插入)function producer($orderId,$redis) { $redis->rPush(task_queue, $orderId); print_r("<br/>追加入列<br/>"); print_r($or…

redis消息队列案例

一、代码实例://需要成功引入并使用redis$redis = RedisManager::$redis;// 生产者:将订单加入队列(右端插入)function producer($orderId,$redis) { $redis->rPush(task_queue, $orderId); print_r("<br/>追加入列<br/>"); print_r($or…