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

题目大意

简化题意

在古老的平方王国,居民 cc = 1, 2, 3, ...)居住在一根高度为 的石柱上。

为了方便居民互相访问,国王建造了不同长度的梯子:

  • 长度为 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 扇门,每扇门后对应一条通向其他房间的走廊,且从同一房间出发的走廊通向不同房间,整个博物馆是连通的(任意两房间可通过走廊连通 )。

要给房间门贴标签,规则是:若房间 udₐ 扇门,门标签为 1dₐ 。游客从房间 u 出发时,先选标签 1 的门进入对应走廊;后续在房间 u 时,若从标签 i 的门进入,就选标签为 i + 1i < dₐ 时 )或 1i = dₐ 时 )的门进入对应走廊。

现在门标签已确定,需对每个房间,计算游客按规则长时间游览时,会经过的不同走廊数量。

输入

  • 第一行是房间数 n3 ≤ n ≤ 2×10⁵ )。
  • 接下来 n 行,每行描述一个房间的走廊:先是 dₐ(该房间门数量,1 ≤ dₐ ≤ 3 ),接着是 dₐ 个整数,按门标签顺序,是这些门通向的房间编号(vᵢ 满足 1 ≤ vᵢ ≤ nvᵢ ≠ u 且不同门通向房间不同 )。
  • 走廊是双向的,若 uv 有门,vu 也有对应门。

思路

按照题目意思模拟就行了

点击查看代码
#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;
}
posted @ 2025-08-22 16:36  归游  阅读(12)  评论(0)    收藏  举报