1.括号序列
链接:https://ac.nowcoder.com/acm/contest/120455/B
题目描述
合法括号序列的定义是:
1.空序列是合法括号序列
2.如果 S 是一个合法括号序列,那么(S)是合法括号序列
3.如果 A 和 B 都是合法括号序列,那么 AB 是一个合法括号序列
现在给定一个括号序列,求最少删去几个括号能得到一个合法的括号序列
输入包含 T 组数据,每组数据中,设括号序列的长度为 N
1≤T,ΣN≤1,000,000
(由于空串是合法的括号序列,所以答案可以是N)
输入描述:
第一行一个数字 T
接下来 T 组数据共 2T 行,每组数据第一行是 N
第二行则是一个长度为 N 的括号序列
输出描述:
T 行 T 个数字,表示输入的括号序列最少删去几个括号能得到一个合法的括号序列
输入
2
6
())(()
9
()(()()))
输出
2
1
思路
问删几个括号能得到一个合法的括号序列,括号""( ""就扔进去然后遇到" )"就看栈里面有能匹配的不,有的话就弹出来栈顶元素,没的话,这就是要删掉的括号,就ans++,然后当循环结束就看栈里面还有多少元素,然后让其和ans相加就行
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
stack<int>st;
int n;
cin>>n;
string s;
cin>>s;
int ans=0;
for(int i=0;i<s.size();i++){
if(s[i]=='('){
st.push(s[i]);
}
else{
if(st.empty()){
ans++;
}
else{
st.pop();
}
}
}
cout<<ans+st.size()<<endl;
}
}
2.十字阵列
链接:https://ac.nowcoder.com/acm/contest/120455/C
来源:牛客网
题目描述
小 Q 新学会了一种魔法,可以对一个 N行M列 的网格上的敌人造成伤害
第 i 次使用魔法可以对网格上的一个十字形区域(即第 xi 行和第 yi 列的并)中的每个格子上的敌人造成 zi 点伤害
现在小 Q 一共使用了 H 次魔法,你需要在所有的施法完成之后统计造成伤害的情况,详见输出描述
提醒:本题输入规模较大,请使用高效的输入方式
1≤H≤500,000 1≤xi,yi,zi,N,M≤2000 1≤xi≤N,1≤yi≤M
输入描述:
第一行 3 个数字 N,M,H
接下来 H 行,每行 3 个正整数 xi,yi,zi
输出描述:
为了避免大量的输出,假设第 i 行第 j 列受到的总伤害是 wij
你只需要输出Σwij(i+j)对 10^9+7 取模的结果即可
输入
5 5 5
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5输出
890
说明
造成伤害的情况是:
1 3 4 5 6
3 2 5 6 7
4 5 3 7 8
5 6 7 4 9
6 7 8 9 5
补充的说明:
890 = 1*(1+1)+3*(1+2)+4*(1+3)+...+5*(5+5),一共25项累加得到890
思路
根据题目描述,我们需要计算所有网格点 (i, j) 的伤害值 w_ij 乘以 (i+j) 的总和,即 Σ[w_ij * (i+j)]。直接使用两个循环遍历所有网格点计算会导致时间复杂度为 O(n*m),在 n 和 m 可能很大的情况下(如达到 10^5 级别)是不可行的。
所以我们用别的方法
-
预处理操作:读取每个操作 (x, y, z),并累加:
-
a[x]:第 x 行所有操作的总伤害 -
b[y]:第 y 列所有操作的总伤害 -
S:所有操作的总伤害之和
-
-
计算加权和:
-
sumi:所有行的索引 i 与对应行总伤害 a[i] 的乘积之和 -
sumj:所有列的索引 j 与对应列总伤害 b[j] 的乘积之和
-
-
组合结果:使用公式:ans = ((m-1)*sumi + (n-1)*sumj + S*(T_m + T_n)) % mod
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[2020],b[2020];
const int mod=1e9+7;
signed main() {
int n,m,h;
cin>>n>>m>>h;
int S=0;
while(h--){
int x,y,z;
cin>>x>>y>>z;
a[x]=(a[x]+z)%mod;
b[y]=(b[y]+z)%mod;
S=(S+z)%mod;
}
int sumi=0;
for(int i=1;i<=n;i++){
sumi=(sumi+i*a[i])%mod;
}
int sumj=0;
for(int i=1;i<=m;i++){
sumj=(sumj+i*b[i])%mod;
}
int ans=((m-1)*sumi%mod+(n-1)*sumj%mod+S*((m*(m+1)/2%mod+n*(n+1)/2%mod)%mod)%mod)%mod;
cout<<ans;
return 0;
}
3.配对
链接:https://ac.nowcoder.com/acm/contest/120455/D
题目描述
现在有正整数集合 A 和 B,每个集合里有 N 个数,你要建立他们间的一一映射
将每对配对的数字相加可以得到 N 个和,你要做的就是最大化第 K 大的和
1≤K≤N≤100,000 输入的所有数字不超过 108
输入描述:
第一行 2 个数字 N,K
接下来两行,每行 N 个正整数,分别表示 A 和 B 中的元素
输出描述:
一行,表示第 K 大的和的最大值
示例1
输入
3 2 1 2 3 1 2 3
3 2 1 2 3 1 2 3输出
5
5
思路
-
排序策略:
-
A 和 B 都按升序排序
-
外层循环
j从 B 的最大值开始向前遍历(从大到小) -
内层循环
i从 A 的最小值开始向后遍历(从小到大)
-
-
贪心匹配逻辑:
-
对于每个 B[j](从大到小),我们尝试找到最小的 A[i] 使得 A[i] + B[j] ≥ x
-
while (i < n && A[i] + B[j] < x)的意思是:-
如果当前的 A[i] 太小,无法与 B[j] 配对达到 x,就跳过它
-
继续检查下一个更大的 A[i]
-
-
一旦找到满足条件的 A[i],就计数并标记该 A[i] 已被使用、
-
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, k;
vector<int> A, B;
bool check(int x) {
int count = 0;
int i = 0;
for (int j = n - 1; j >= 0; j--) {
while (i < n && A[i] + B[j] < x) {
i++;
}
if (i < n) {
count++;
i++;
}
}
return count >= k;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
A.resize(n);
B.resize(n);
for (int i = 0; i < n; i++) {
cin >> A[i];
}
for (int i = 0; i < n; i++) {
cin >> B[i];
}
sort(A.begin(), A.end());
sort(B.begin(), B.end());
int left = 1, right = 200000001;
while (left < right) {
int mid = left + (right - left) / 2;
if (check(mid)) {
left = mid + 1;
} else {
right = mid;
}
}
cout << left - 1 << endl;
return 0;
}
4.无穷无尽的字符串
链接:https://ac.nowcoder.com/acm/contest/123788/B
题目描述
\hspace{15pt}小红有无穷无尽的字符串。
\hspace{15pt}小红拿到了一个由无限个 abc\texttt{abc}abc 依次拼接而成的字符串 abcabcabc...\texttt{abcabcabc}\dotsabcabcabc...,她想知道从字符串的第 lll 位到第 rrr 位中,a,b,ca,b,ca,b,c 分别出现了多少次,请你帮帮她。
输入描述:
第一行输入两个整数
𝑙,𝑟(1≦𝑙≦𝑟≦10^9)l,r(1≦l≦r≦10^9 )。
输出描述:
输出三个整数,分别代表 a,b,ca,b,ca,b,c 出现的次数。示例1
输入
复制1 3
1 3输出
复制1 1 1
1 1 1
思路
字符串是无限重复的"abc",每个位置pos(从1开始)的字符规则是:
pos mod 3 = 1 → 'a'
pos mod 3 = 2 → 'b'
pos mod 3 = 0 → 'c'
对于任意整数 n,在 1 到 n 的范围内:
-
模3余0的数的个数:
n/3 -
模3余1的数的个数:
(n+2)/3 -
模3余2的数的个数:
(n+1)/3cpp#include <iostream> using namespace std; int l; int r; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>l>>r; l--; for(int i=2;i>=0;i--){ cout<<(r+i)/3-(l+i)/3<<' '; } return 0; }
5.米斯蒂娅不想被吃掉
链接:https://ac.nowcoder.com/acm/contest/124143/B
题目描述
链接:https://ac.nowcoder.com/acm/contest/124143/B
来源:牛客网
米斯蒂娅经常去摆烧烤摊,幽幽子是烧烤摊的常客。
幽幽子每天都要吃 x 吨烧烤才会满意,在 n 天内米斯蒂娅每天会拿到若干吨食材,她可以将其中一部分做成烧烤,但食材最多只能存放 2 天(也就是说只能用当天或者前一天的食材做烧烤)。如果某天幽幽子不满意就会把米斯蒂娅吃掉。米斯蒂娅想知道是否存在一种烧烤策略,使得自己在 𝑛 天内不会被吃掉,请你帮帮她。
我们假设食材做成烧烤的过程是没有重量变化的。
输入描述:
每个测试文件均包含多组测试数据。第一行输入一个整数 𝑇 (1≦𝑇≦10)T (1≦T≦10) 代表数据组数,每组测试数据描述如下:第一行输入两个整数𝑛,𝑥(1≦𝑛≦2×105,1≦𝑥≦2×105)n,x(1≦n≦2×10^5,1≦x≦2×10^5 )。第二行输入n 个整 数 𝑎𝑖(0≦𝑎𝑖≦2×10^5)a i(0≦a i ≦2×10^5 ),代表第𝑖天拿到的食材吨数。
输出描述:
对于每组测试数据,新起一行。如果存在一种策略使得米斯蒂娅不会被吃掉,请输出 Yes;否则输出 No。输入
2
3 1
2 0 1
3 2
6 0 1
输出
Yes
No
说明
对于第一组数据,米斯蒂娅可以每天都做一吨烧烤。
对于第二组数据,第三天一定无法做出两吨烧烤。
思路:比较简单哈,需要注意到是别遇到No的情况就直接return了,必须要让人家给输入完,要不然就报错
cpp
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n,x;
cin >> n >> x;
int per = 0,flag = 0;
for(int i = 1;i <= n;i ++){
int t;
cin >> t;
if(t + per < x){
flag = 1;
// break;
}
// per
if(per >= x){
per = t;
}else{
per = t - (x - per);
}
}
if(flag == 0)
cout << "Yes"<<endl;
else cout <<"No\n";
return;
}
int main(){
int T;
cin >> T;
while(T --){
solve();
}
return 0;
}