Contest2897 - 2023寒假集训-进阶训练赛(一)

问题 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≤iM)条边连接点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≤Mmin(2N(N−1),1000)

  • 1 ≤ a i , b i ≤ N 1leq a_i,b_ileq N1≤ai,biN

  • 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的莲花上。

你将进行如下操作若干次:

  1. 选定任意的2个正整数A, B,初始分数为0。假设当前位置为x:

  1. 你的坐标移动到 x + A 这个位置上 , 并且y = x + A

  • 如果y=N−1,游戏结束。

  • 如果y≠N−1,但是y这个莲花存在,那么分数增加si,并且这片莲花消失。

  • 如果y≠N−1,但是y这个莲花不存在,那么分数减去10的100次方,游戏结束。

  1. 你的坐标移动到 x - B 这个位置上 , 并且y = x - B

  • 如果y=N−1,游戏结束。

  • 如果y≠N−1,但是y这个莲花存在,那么分数增加si,并且这片莲花消失。

  • 如果y≠N−1,但是y这个莲花不存在,那么分数减去10的100次方,游戏结束。

  1. 然后不断重复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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗯嗯你说的对

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值