2025牛客暑期多校训练营1
Symmetry Intervals
题目大意
给定一个长度为 n 的字符串 S,你需要回答 q 次询问。
每次询问给出:
- 一个字符串
T; - 一个整数
a(满足1 ≤ a ≤ n - |T| + 1,其中|T|是T的长度)。
你需要统计满足以下条件的区间 [u, v](1 ≤ u ≤ v ≤ |T|)的数量:
- 对于所有
x ∈ [u, v],都有S[a + x - 1] == T[x]。
换句话说,你需要统计 T 的所有子串中,有多少个子串与 S 中从位置 a 开始匹配的子串完全一致。
思路
总的来说,其实就是找所有满足条件的公共子序列的数量
点击查看代码
//2025牛客暑期多校训练营1
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
ll n, q;
cin >> n >> q;
string s;
cin >> s;
while (q--) {
string t;
ll a, ans = 0;
cin >> t >> a;
for (ll i = 0; i < t.size(); i++) {
ll j = i;
while (s[j + a - 1] == t[j] && j < t.size()) {
j++;
}
ans += (j - i + 1) * (j - i) / 2;
i = j;
}
cout << ans <<"\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _ = 1;
// cin >> _;
while (_--) {
solve();
}
return 0;
}
Endless Ladders
题目大意
简化题意
在古老的平方王国,居民 c(c = 1, 2, 3, ...)居住在一根高度为 c²的石柱上。
为了方便居民互相访问,国王建造了不同长度的梯子:
- 长度为
d的梯子可以连接高度差恰好为d的两个居民。 - 由于预算有限,只有当存在两个居民的高度差恰好为
d时,才会建造一个长度为d的梯子,且每种长度的梯子只建一个。 - 梯子按长度从小到大依次编号为
1, 2, 3, ...。
有一天,居民 a 想访问居民 b,你需要帮他们找到应该使用的梯子的编号。
换句话说:
- 计算
|a² - b²|(即|a² - b²|就是所需梯子的长度d)。 - 在所有可能的
d中,按从小到大排序后,d = |a² - b²|是第几个?
思路
打表找规律
可以得知梯子长度从小到大为:
3,5,7,8,9,11,12,13,15,17,19,21....
不是梯子长度的
1,2,4,6,10,14,18,22,26,30,34,...
发现规律是从6开始,不断加4
所以求梯子长度只需要梯子长度减去不是梯子长度的个数即可
点击查看代码
//2025牛客暑期多校训练营1
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll a,b;
void solve() {
cin>>a>>b;
if(a>b) swap(a,b);
ll c=b*b-a*a;
ll x=max(c-6,0ll);
cout<<c-4-x/4+(c<6)+(c<4)<<endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _ = 1;
cin >> _;
while (_--) {
solve();
}
return 0;
}
Numb number
题目大意
每组测试用例有 n 个数字,每个数字每天会和其他 n-1 个数字竞争,若它输掉至少⌈(n-1)/2⌉场(即比对手小)则成为 “失败麻木者”;每天有一个数字会增加指定数值,需输出每次更新后 “失败麻木者” 的数量。输入包含 T 组测试用例,每组有 n(3≤n≤2×10⁵)、q(1≤q≤2×10⁵)、n 个数字的初始值及 q 次更新(每次指定数字编号和增加的数值),所有测试用例的 n 和 q 的总和均不超过 5×10⁵。
思路
思路很简单,实现很难
但是这里有一个现成的__gnu_pbds库
点击查看代码
#include <bits/stdc++.h>
#include <bits/extc++.h> // 包含__gnu_pbds库
using namespace std;
using namespace __gnu_pbds; // 使用扩展库命名空间
// 定义长整型和pair类型别名,简化代码
using ll = long long;
using pll = pair<ll, ll>;
// 定义有序树数据结构,支持顺序统计
// 存储类型:pll(pair<ll, ll>),即(数值, 索引),确保元素唯一性
// 排序方式:按pair升序(先比数值,数值相同则比索引)
// 底层实现:红黑树,支持顺序统计操作
using ordered_set = tree<
pll,
null_type, // 无映射值,类似set
less<pll>, // 比较器
rb_tree_tag, // 红黑树标签
tree_order_statistics_node_update // 启用顺序统计功能
>;
const int MAXN = 2e5 + 10; // 最大数据规模
ll a[MAXN]; // 存储每个数字的当前值
void solve() {
int n, q;
cin >> n >> q; // 读取数字数量和更新次数
ordered_set tr; // 声明有序树
// 初始化:读取初始值并插入有序树
for (int i = 1; i <= n; ++i) {
cin >> a[i];
tr.insert({a[i], i}); // 插入(值, 索引)确保唯一性
}
// 处理每次更新
while (q--) {
int p; // 要更新的数字编号
ll v; // 增加的数值
cin >> p >> v;
// 1. 先从有序树中删除旧值
tr.erase({a[p], p});
// 2. 更新数字的值
a[p] += v;
// 3. 将新值插入有序树
tr.insert({a[p], p});
// 4. 计算失败麻木者数量
// 失败麻木者条件:输掉的场次 >= ceil((n-1)/2)
// 等价于:比它大的数字数量 >= ceil((n-1)/2)
// 进一步等价于:该数字 <= 第k小的数字,其中k = (n+1)/2 (0-based)
ll k = (n + 1) / 2; // 计算阈值位置
auto threshold = tr.find_by_order(k); // 找到第k小的元素
ll t = threshold->first; // 阈值元素的数值
// 统计所有小于t的元素数量,即失败麻木者的数量
// 使用{t, -1}是因为索引最小为1,确保所有值为t的元素都不被计入
ll count = tr.order_of_key({t, -1});
// 输出结果
cout << count << '\n';
}
}
int main() {
// 优化输入输出速度
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t; // 读取测试用例数量
while (t --) {
solve(); // 处理每个测试用例
}
return 0;
}
Mesume Acceptance
题目大意
有一个由 n 个房间和双向走廊组成的博物馆,每个房间最多有 3 扇门,每扇门后对应一条通向其他房间的走廊,且从同一房间出发的走廊通向不同房间,整个博物馆是连通的(任意两房间可通过走廊连通 )。
要给房间门贴标签,规则是:若房间 u 有 dₐ 扇门,门标签为 1 到 dₐ 。游客从房间 u 出发时,先选标签 1 的门进入对应走廊;后续在房间 u 时,若从标签 i 的门进入,就选标签为 i + 1(i < dₐ 时 )或 1(i = dₐ 时 )的门进入对应走廊。
现在门标签已确定,需对每个房间,计算游客按规则长时间游览时,会经过的不同走廊数量。
输入:
- 第一行是房间数
n(3 ≤ n ≤ 2×10⁵)。 - 接下来
n行,每行描述一个房间的走廊:先是dₐ(该房间门数量,1 ≤ dₐ ≤ 3),接着是dₐ个整数,按门标签顺序,是这些门通向的房间编号(vᵢ满足1 ≤ vᵢ ≤ n、vᵢ ≠ u且不同门通向房间不同 )。 - 走廊是双向的,若
u到v有门,v到u也有对应门。
思路
按照题目意思模拟就行了
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=2e6+10;
int num[maxn],d[maxn][4];
int ans[maxn][4];//第i个节点从第j个z走廊所在的最大圈
bool book[maxn][4];//第i个节点是否经过第j个走廊
map<int,map<int,bool> > mp;
int dfs(int u,int x,int len){//u需要通过第x走廊离开
book[u][x]=1;
int v=d[u][x],s;//v是从u出发经过第x走廊到达的点
for(int j=0;j<num[v];++j){
if(d[v][j]==u) s=(j+1)%num[v]; //v的第j个走廊通向u。所以s按照题目要求取
}
int tt=mp[u][v];// 记录这条走廊是否第一次走
mp[u][v]=mp[v][u]=1;//这条走廊经过
if(book[v][s]) // 如果下一条门的状态已经算过 → 形成环
return ans[u][x]=(!tt)+len;//若第一次走这条走廊则 +1,再加上之前跨的走廊数
else return ans[u][x]=dfs(v,s,len+!tt);//走过对答案没贡献,没走过有贡献
}
void solve(){
cin>>n;
mp.clear();
for(int i=1;i<=n;++i) {
cin>>num[i];
for(int j=0;j<num[i];++j)
cin>>d[i][j];
}
for(int i=1;i<=n;++i){
if(!ans[i][0]){
mp.clear();
dfs(i,0,0);
}
cout<<ans[i][0]<<endl;
}
return ;
}
int main(){
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}

浙公网安备 33010602011771号