问题 E: 【蓝桥杯2022初赛】统计子矩阵
内存限制:128 MB时间限制:1.000 S评测方式:文本比较命题人:admin提交:304解决:138
题目描述
给定一个 N × M 的矩阵A,请你统计有多少个子矩阵(最小 1 × 1,最大 N × M) 满足:
子矩阵中所有数的和不超过给定的整数K?
输入
第一行包含三个整数N, M 和K.
之后 N 行每行包含 M 个整数,代表矩阵A.
30%的测试数据:1≤N,M≤20;
70%的测试数据:1≤N,M≤100;
100%的测试数据:1≤N,M≤500;0≤Aij≤1000;1≤K≤250000000。
输出
答案
样例输入 复制
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
样例输出 复制
19
提示
满足条件的子矩阵一共有19,包含:
大小为1 × 1 的有10 个。
大小为1 × 2 的有3 个。
大小为1 × 3 的有2 个。
大小为1 × 4 的有1 个。
大小为2 × 1 的有3 个。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[502][502]={0};
int main()
{
memset(dp,0,sizeof(dp));
int n,m,top;
scanf("%d%d%d",&n,&m,&top);
int val;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
scanf("%d",&dp[i][j]);
dp[i][j]+=dp[i-1][j];
};
long long result=0; //result还必须定义成long long,否则百分之二十答案错误
for(int i=1;i<=n;++i) //百度看的"双指针求解"
for(int j=i;j<=n;++j) //现在才看明白,就是tow point
for(int col_l=1,col_r=1,sum=0;col_r<=m;++col_r)
{
sum+=dp[j][col_r]-dp[i-1][col_r];
while(sum>top)
{
sum-=dp[j][col_l]-dp[i-1][col_l];
++col_l;
}
if(col_l<=col_r)
result+=col_r-col_l+1;
}
cout << result << endl;
return 0;
}
问题 I: 跑图
内存限制:256 MB时间限制:2.000 S评测方式:文本比较命题人:admin提交:270解决:92
题目描述
小T给你了一个N NN点M MM边的无向带权连通简单图(无向、带权、连通、无自环、无重边)
其中第i ( 1 ≤ i ≤ M ) i(1leq ileq M)i(1≤i≤M)条边连接点a i a_iai和b i b_ibi,权重是c i c_ici
输出不属于任意两点的最短路径的边的条数。
输入
2 ≤ N ≤ 100 2leq Nleq 1002≤N≤100
N − 1 ≤ M ≤ m i n ( N ( N − 1 ) 2 , 1000 ) N-1leq Mleq min( rac{N(N-1)}{2},1000)N−1≤M≤min(2N(N−1),1000)
1 ≤ a i , b i ≤ N 1leq a_i,b_ileq N1≤ai,bi≤N
1 ≤ c i ≤ 1000 1leq c_ileq 10001≤ci≤1000
c i c_ici 是整数
给定的图没有自环也没有重边
给定的图是连通图
输入的格式是:
N M
a1 b1 c1
a2 b2 c2
...
am bm cm
输出
输出不属于任意两点的最短路径的边的条数。
即:如果点a aa和点b bb的最短路径经过了边c cc,那么c cc不是答案中的一条。
样例输入 复制
3 3
1 2 1
1 3 1
2 3 3
样例输出 复制
1
提示
从点1到点2,最短路径是1->2,权重为1
从点2到点3,最短路径是2->3,权重为1
从点3到点1,最短路径是3->2->1,权重为2
所以第3条边(连接点1和点3,权重是3)不属于上面任何两点之间的最短路径,因此答案是1(条)。
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f
using namespace std;
int d1[105][105];
int d2[105][105];
signed main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(i==j)
{
d1[i][j]=0;
d2[i][j]=0;
}
else
{
d1[i][j]=inf;
d2[i][j]=inf;
}
}
int a,b,c;
for(int i=0;i<m;i++)
{
cin>>a>>b>>c;
a-=1,b-=1;
d1[a][b]=min(d1[a][b],c);
d1[b][a]=min(d1[b][a],c);
d2[a][b]=min(d2[a][b],c);
d2[b][a]=min(d2[b][a],c);
}
for(int k=0;k<n;k++)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(i!=j && i!=k && j!=k)
{
if(d2[i][j]>d2[i][k]+d2[k][j])
d2[i][j]=d2[i][k]+d2[k][j];
}
}
}
}
int ans=0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(d2[i][j]!=d1[i][j]&&d1[i][j]!=inf)
ans++;
cout<<ans<<endl;
}
问题 J: Ensemble’s heritage
内存限制:128 MB时间限制:2.000 S评测方式:文本比较命题人:admin提交:741解决:259
题目描述
作为十三课的卢卡,是没有权力去直接审讯犯人的。所以卢卡就偷到了N张卡,监狱一共有M个房间。
第i个监狱的门可以由 第L,L+1,L+2, ... ,R张卡打开
但是卢卡的数学并不好,他想让你帮帮他,有几张卡能打开所有的门
输入
N M
L1 R1
L2 R2
L3 R3
...
LN RN
1<=N<=1e5 1<=M<=1e5 1<=Li<=Ri<=N
输出
一个整数,代表答案
样例输入 复制
4 2
1 3
2 4
样例输出 复制
2
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct info{
int l=0;
int r=0;
}a[100005];
bool mysort(info a,info b)
{
return a.l<b.l;
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++)
cin>>a[i].l>>a[i].r;
sort(a,a+m,mysort);
int min=1000005;
for(int i=0;i<m;i++)
{
if(a[i].r<min)
min=a[i].r;
}
int ans=min-a[m-1].l+1;
if(ans<0)
cout<<0<<endl;
else
cout<<ans<<endl;
}
问题 K: 珠宝
内存限制:128 MB时间限制:1.000 S评测方式:文本比较命题人:admin提交:266解决:137
题目描述
你的朋友送了你一个序列D
这个序列D上一共有n个珠宝
按照从左到右的顺序
每个珠宝都有它的价值w[i]
1 <= n <= 50
-1e7 <= w[i] <= 1e7
你可以对这些珠宝进行 最多 k次操作
1 <= k <= 100
每次操作你都可以
操作A:取出D中最左边的宝石,放在手中。当D为空时,不能执行此操作。
操作B:取出D中最右边的宝石,放在手中。当D为空时,不能执行此操作。
操作C:选择手中的任意一个宝石并将其插入D的左端。如果手中没有宝石,则无法执行此操作。
操作D:选择手中的任意一个宝石并将其插入D的右端。如果手中没有宝石,则无法执行此操作。
求执行完 最多k次操作之后 你手上有的珠宝的价值之和的最大值
输入
n k
w1 w2 ...... wn
输出
打印一行
执行完 最多k次操作之后 你手上有的珠宝的价值之和的最大值
样例输入 复制
6 4
-10 8 2 1 2 6
样例输出 复制
14
提示
执行操作A。从D的左端取出价值为-10的珠宝。
执行操作B。从D的右端取出价值为6的珠宝。
执行操作A。从D的左端取出价值为8的珠宝。
执行操作D。插入价值为-10的珠宝到D的右端。
你手上现有的珠宝总价值为14
//copy from zzx 2021-2022-1 ACM集训队每周程序设计竞赛(3)题解_ZZXzzx0_0的博客-CSDN博客_对于灯泡i连接的这些开关中如果打开的开关数量%2等于p[i],灯泡i会被点亮。 开关的
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 2010 , mod = 1e9 + 7 ;
int n , k ;
int a[N] ;
signed main()
{
cin >> n >> k ;
fer(i,1,n) sf(a[i]) ;
int res = 0 ; // 答案
for(int i = 0 ; i <= n ; i ++) // 左边选i个
{
for(int j = 0 ; j <= n ; j ++) // 右边选j个
{
if(i + j > k) continue ; // 如果操作数大于k了 就退出
multiset<int> q ; // 包含重复元素的set(set内元素从小到大自动排序)
int s = 0 ; // 这种选法的最大值
// 左边选i个
for(int l = 1 ; l <= i ; l ++)
{
q.insert(a[l]) ;
s += a[l] ;
}
// 右边选j个
for(int r = n ; r >= n - j + 1 && r > i ; r --)
{
q.insert(a[r]) ;
s += a[r] ;
}
if(i + j < k) // 如果操作数不够k个 , 剩下的操作用来删除
{
int d = k - (i + j) ; // 可以用来删除的操作数
while(d) // 只要 d > 0 就可以删
{
if(*q.begin() >= 0) break ; // 如果最小的元素大于等于0 说明不用删除
if(*q.begin() < 0) // 反之删除
{
s -= *q.begin() ;
q.erase(q.begin()) ;
}
d -- ;
}
}
res = max(res,s) ; // 更新答案
}
}
cout << res << "\n" ;
return 0;
}
问题 L: 道路工程
内存限制:128 MB时间限制:2.000 S评测方式:文本比较命题人:admin提交:119解决:31
题目描述
有一条从西到东的无限长的街道,我们认为这是一条无限长的数轴
这条街上计划进行N项道路工程。
第i个道路工程在坐标Xi处阻段该点 阻挡的时间从Si - 0.5 到 Ti - 0.5
也就是说在 Si - 0.5 到 Ti - 0.5 这段时间内 Xi 这个点不能通过
第i个人将在时间Di开始从坐标0 , 不断以速度1个单位每秒正向行走,到达阻段点时停止行走。
求出每个人将要走的距离
如果这个人走的过程中不会遇到阻断点
则输出-1
1 <= N , Q <= 2e5 (200000)
0 <= Si < Ti <= 1e9 (1000000000)
1 <= Xi <= 1e9
0 <= D1 < D2 < D3 < ....... < Dq <= 1e9
输入
N Q
S1 T1 X1
S2 T2 X2
...........
Sn Tn Xn
D1
D2
.....
Dq
输出
每行一个数
输出第i个人将要走的距离
如果这个人走的过程中不会遇到阻断点
则输出-1
样例输入 复制
4 6
1 3 2
7 13 10
18 20 13
3 4 2
0
1
2
3
5
8
样例输出 复制
2
2
10
-1
13
-1
这道题给了我一个很好的思考方式:当我们正向的去面向对象求解的话时间复杂度会很高,那么这时候我们就可以考虑去从别的东西出发,逆向去求解。
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct info{
int l;
int r;
int x;
}a[200005];
bool cmp(info a,info b)
{
return a.x<b.x;
}
set<pair<int,int>>se;
int ans[200005];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,q;
cin>>n>>q;
for(int i=0;i<q;i++)
ans[i]=-1;
for(int i=0;i<n;i++)
{
int l,r,x;
cin>>l>>r>>x;
a[i]={l-x,r-x-1,x};
}
sort(a,a+n,cmp);
for(int i=0;i<q;i++)
{
int t;
cin>>t;
se.insert({t,i});
}
for(int i=0;i<n;i++)
{
int l=a[i].l,r=a[i].r,x=a[i].x;
while(se.size())
{
auto it=se.lower_bound({l,-2e9});
if(it==se.end())
break;
if(it->first>r)
break;
ans[it->second]=x;
se.erase(it);
}
}
for(int i=0;i<q;i++)
cout<<ans[i]<<endl;
}
问题 M: 蛙跳
内存限制:128 MB时间限制:1.000 S评测方式:文本比较命题人:admin提交:74解决:24
题目描述
小T在第七共和国的中心大楼发生了爆炸之后重生了,他发现他现在是一只青蛙,如果要重新变回人类,他需要解决这道问题
有无限广阔的池塘,可以看作是一条直线。
这个池塘里漂浮着n个莲花,它们的坐标为0,1,.....n-1.。你在最初坐标0的莲花上。
你将进行如下操作若干次:
选定任意的2个正整数A, B,初始分数为0。假设当前位置为x:
你的坐标移动到 x + A 这个位置上 , 并且y = x + A
如果y=N−1,游戏结束。
如果y≠N−1,但是y这个莲花存在,那么分数增加si,并且这片莲花消失。
如果y≠N−1,但是y这个莲花不存在,那么分数减去10的100次方,游戏结束。
你的坐标移动到 x - B 这个位置上 , 并且y = x - B
如果y=N−1,游戏结束。
如果y≠N−1,但是y这个莲花存在,那么分数增加si,并且这片莲花消失。
如果y≠N−1,但是y这个莲花不存在,那么分数减去10的100次方,游戏结束。
然后不断重复2 3操作
问你如何选择A,B 可以让最后的得分最大,输出该得分
输入
n
s0 s1 .......sn−1
1 <= n <= 1e5(100000)
-1e9 <= si <= 1e9 (1后面9个0)
s0 = sn−1 = 0
输出
问选定最优的A、B的情况下,得到的最高分数为多少?
样例输入 复制
5
0 2 5 1 0
样例输出 复制
3
提示
如果选择A=3和B=2,
游戏按如下方式进行:
移动到坐标0 + 3 = 3。
你的分数增加s3 = 1分 .
移到坐标3−2=1.
你的分数增加s1 =2.
移动到坐标1+3=4。
4 = n - 1 所以
比赛以3分结束。
没有办法以4分或更高的分数结束比赛,所以答案是3分。
请注意,您不能在坐标2处降落莲花,否则稍后会溺水。
copy from zzx
#include <bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e5 + 10 , M = 2010 , mod = 1e9 + 7 ;
int n ;
int s[N] ;
int used[N] ;
signed main()
{
cin >> n ;
fer(i,0,n-1) sf(s[i]) ;
int res = 0 ;
for(int c = 1 ; c <= n ; c ++)
{
int ans = 0 ;
for(int k = 1 ; k * c < n ; k ++)
{
int a = k * c;
int b = n - 1 - k * c;
int A = b, B = b - c;
if (A <= 0 || B <= 0) break;
if (a < 0 || a >= n || b < 0 || b >= n || a == b) break;
if (used[a] == c || used[b] == c)
{
break;
}
used[a] = c;
used[b] = c;
ans += s[a];
ans += s[b];
res = max(res, ans);
}
}
cout << res << "\n" ;
return 0;
}