【题解】day2

A:蓝蓝的棋盘

时间限制: 1 s 1s 1s 空间限制: 128 M B 128 MB 128MB

【题目描述】

A A A淘淘和蓝蓝在下棋.

这个棋盘是 1 × n 1\times n 1×n 的,棋盘的第 i i i 个格子上有一个数 a [ i ] a[i] a[i] ,因此我们可以把棋盘看做一个序列.一开始棋子在位置 0 0 0 ,双方得分都是 0 0 0 .

双方轮流操作棋子,如果当前棋子的位置是 p p p ,可以选择把棋子移动到 [ p + 1 , m i n ( n , p + m ) ] [p+1,min(n,p+m)] [p+1,min(n,p+m)] 的范围内,即**最多往右移动 m m m格,**但是不能移出棋盘.

每次操作之后,当前移动棋子的人会获得移动到的格子上数字的分数.

当棋子被移动到位置 n n n 的时候(同时取 n n n 的分数),游戏就结束了.

淘淘和蓝蓝都会按照自己的最优策略移动棋子.蓝蓝想知道,当他先手的时候,他的分数减去淘淘的分数是多少.(最优化策略指最大化自己的得分减对方的得分)

【输入格式】

1 1 1 行,两个整数 n n n m m m ,用空格隔开.

2 2 2 行, n n n个整数, A [ 1 ] . . A [ n ] A[1] .. A[n] A[1]..A[n],用空格隔开.

【输出格式】

一行一个整数表示答案.

【样例输入1】

1 9 
1

【样例输出1】

1

【样例输入2】

5 3
-1 5 -9 8 1

【样例输出2】

-2

【样例输入3】

10 3
1 -3 4 -7 22 -12 34 -4 -7 -9

【样例输出3】

14

【样例解释】

在样例 1 1 1 中,蓝蓝只能移动到第 1 1 1 个格子,答案为 1 − 0 = 1 1-0=1 10=1

在样例 2 2 2 中,蓝蓝先移动到 2 2 2 ,淘淘移动到 4 4 4 ,蓝蓝再移动到 5 5 5 .棋子的移动路径是 0 − > 2 − > 4 − > 5. 0->2->4->5. 0>2>4>5.

在样例 3 3 3 中,棋子的路径是 0 − > 3 − > 5 − > 7 − > 9 − > 10. 0->3->5->7->9->10. 0>3>5>7>9>10.

【数据范围】

对于 30 % 30\% 30% 的数据, n ≤ 15 ; n≤15; n15;

对于 80 % 80\% 80% 的数据, m ≤ 100 ; m≤100; m100;

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 500000 , 1 ≤ m ≤ 998244353 , − 998244353 ≤ a [ i ] ≤ 998244353. 1≤n≤500000,1≤m≤998244353,-998244353≤a[i]≤998244353. 1n500000,1m998244353,998244353a[i]998244353.

【算法分析】

30 30 30

暴力枚举所有情况。

80 80 80

D P DP DP

d p [ x ] dp[x] dp[x] 表示从 x x x 走到 n n n ,最优策略下,先手减去后手的分数。

x x x 走到 y y y ,若 x x x 蓝蓝先手走到 y y y ,只考虑后面一段 y − > n y->n y>n 这一段,也可以看做淘淘先手,在最优策略下,先手减去后手的分数,在蓝蓝视角就是后手减去先手分分数。当问题规模变化,先手也是相对的。

因此,若 x x x 先手走到 y y y d p [ y ] dp[y] dp[y] 可以表示后手从 y y y 走到 n n n ,最优策略下,后手减去先手的分数,得到状态转移方程:

d p [ x ] = m a x ( a [ y ] − d p [ y ] ) , x < y < m i n ( x + m , n ) dp[x]=max(a[y]-dp[y]),x<y<min(x+m,n) dp[x]=max(a[y]dp[y]),x<y<min(x+m,n)

枚举 y y y 进行状态转移,时间复杂度为 O ( n m ) O(nm) O(nm)

100 100 100

要找到 x < y < m i n ( x + m , n ) , a [ y ] − d p [ y ] x<y<min(x+m,n),a[y]-dp[y] x<y<min(x+m,n)a[y]dp[y] 的最大值,可以使用单调队列优化,使用队列保存 a [ y ] − d p [ y ] a[y]-dp[y] a[y]dp[y] ,维护单调递减队列,最大值就是队头。

【参考程序】

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=500010;
ll a[N],f[N];
int n,m; 
ll q[N];        //优先队列 
int l,r,num[N];
void q_push(ll x,int t)
{
    while(l<=r&&q[r]<=x) r--; //单调第减
    q[++r]=x;
    num[r]=t;       //当前值所在的位置 
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);    
    f[n]=0;
    l=1;r=0;
    q_push(a[n]-f[n],n); 
    for(int i=n-1;i>=0;i--)
    {
         
        while(num[l]>i+m) l++; //找满足情况的最大值 
        f[i]=q[l];
        q_push(a[i]-f[i],i);    //入队 
    }
    cout<<f[0]<<endl;
}

B:淘淘的集合

时间限制: 2 s 2s 2s 空间限制: 1024 M B 1024 MB 1024MB

【题目描述】

淘淘有 n n n 个数字 a [ 1 ] . . . a [ n ] a[1]...a[n] a[1]...a[n] n n n 个集合,一开始所有的 a [ i ] a[i] a[i] 都是 0 0 0 ,第 i i i 个集合初始为 i {i} i .

淘淘有时候会合并两个集合或者给一个集合里的所有数整体加上一个值.

蓝蓝觉得淘淘的数字太大了,有时会把 a [ l . . r ] a[l..r] a[l..r] 都赋值为0,有时会询问一段区间的数字的和。

形式化的讲,就是有四种事件会发生:

1   x   y 1 \ x \ y 1 x y 合并数字 x x x 所在的集合和 y y y 所在的集合.如果 x x x y y y 已经在同一个集合,则没有变化;

2   x   v 2\ x\ v 2 x v x x x 所在的集合为 S S S ,对于任意 y ∈ S ( y ∈ S y∈S(y∈S yS(yS y y y 在集合 S S S ) , a [ y ] + = v ),a[y]+=v ),a[y]+=v

3   l   r 3\ l\ r 3 l r a [ l ] . . . a [ r ] a[l]...a[r] a[l]...a[r] 都赋值为 0 0 0 ;

4   l   r 4\ l\ r 4 l r 询问 a [ l ] . . . a [ r ] a[l]...a[r] a[l]...a[r] 的和.

一共会发生 m m m 次这样的事件,你能正确解答蓝蓝的每次询问吗?

【输入格式】

1 1 1 行,两个整数 n n n m m m ,用空格隔开.

2 ∼ ( m + 1 ) 2\sim (m+1) 2(m+1) 行,每行三个整数表示一个操作.

保证事件 4 4 4 ( r − l + 1 ) (r-l+1) (rl+1) 的和不超过 10000000 10000000 10000000.

【输出格式】

对于每个事件 4 4 4 ,输出一行一个整数表示询问的答案.

【样例输入】

3 10
2 1 -7
1 1 3
2 3 5
4 1 3
3 2 3
4 1 3
2 3 -7
4 1 3
2 2 2
4 1 3

【样例输出】

3
-2
-16
-14

【数据范围】

对于 30 % 30\% 30% 的数据, n , m ≤ 3000 ; n,m≤3000; n,m3000;

对于另外 10 % 10\% 10% 的数据,没有事件 1 ; 1 ; 1;

对于另外 20 % 20\% 20% 的数据,没有事件 3 ; 3; 3;

对于另外 20 % 20\% 20% 的数据,保证事件 3 3 3 ( r − l + 1 ) (r-l+1) (rl+1) 的和不超过 10000000 10000000 10000000 .

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 200000 , 1 ≤ m ≤ 50000 ; 1≤n≤200000,1≤m≤50000; 1n200000,1m50000; 事件 1 , 2 1,2 1,2 保证 1 ≤ x , y ≤ n , − 998244353 ≤ v ≤ 998244353 ; 1≤x,y≤n,-998244353≤v≤998244353; 1x,yn,998244353v998244353; 事件 3 , 4 3,4 3,4 保证 1 ≤ l ≤ r ≤ n ; 1≤l≤r≤n; 1lrn; 保证事件 4 4 4 ( r − l + 1 ) (r-l+1) (rl+1) 的和不超过 10000000. 10000000. 10000000. 数据保证所有事件 4 4 4 的答案不超过 C + +   l o n g   l o n g C++\ long\ long C++ long long 的范围。

【算法分析】

题目给出了 保证事件 4 4 4 ( r − l + 1 ) (r-l+1) (rl+1) 的和不超过 10000000 10000000 10000000.,那么可以将区间询问,看做单点询问,总共不会超过 10000000 10000000 10000000 次。

对于事件 1 , 2 1,2 1,2 ,使用并查集维护集合,集合的合并使用启发式合并,将深度小的树(集合)接到深大大的树(集合),这样最大深度为 log ⁡ n \log n logn 。对于集合修改,可以直接在根节点打标记,不用一个点进行修改。当需要查询某个值 s s s时,在 log ⁡ n \log n logn 的时间可以找到集合的根节点,同时将根节点的标记计算在内,就能得到秀嘎哈 s s s 的值。

对于事件 3 3 3 ,区间赋值为 0 0 0 ,可以记录每个点离查询最近的一次赋值为 0 0 0 的值,若 a [ x ] a[x] a[x] 在第 i i i 次赋值为 0 0 0 ,赋值为 0 0 0 时的值为 V i V_i Vi ,在第 j j j 次查询,此时的值为 V j V_j Vj (不考虑赋值为 0 0 0 ),那么 a [ x ] a[x] a[x] 实际的值就是 V j − V i V_j-V_i VjVi 。因此。对于区间赋值为 0 0 0 不作修改,只记录赋值为 0 0 0 的时间,通过询问时的值 − - 赋值 0 0 0 前的值,就得到当前的值。但是如果每次区间赋值为 0 0 0 都进行操作,就需要访问区间每一个点,规定时间内肯定无法完成。

结合事件 4 4 4 来处理事件 3 3 3 ,事件 4 4 4 最多访问 10000000 10000000 10000000 个点,而且最近一次赋值为 0 0 0 的时间才有用,这里使用离线操作,将赋值为 0 0 0 的时刻和询问时刻都记录下来。

如何处理区间赋值为 0 0 0 呢,这里使用分块的方法,区间赋值为 0 0 0 相当于区间修改。当进行询问时,此时先记录询问的点是否有赋值为 0 0 0 的,如果有就逐个记录下来赋值为 0 0 0 的时间,然后,记录询问点的时间,这样,即使有点在重复赋值为 0 0 0 ,也只记录一次最近的一次赋值为 0 0 0 的时间。

然后,按照时间来依次处理,赋值为 0 0 0 ,也可以看作一次询问,接着是真正的询问,两者相减就是实际的值,时间复杂度为 ( 10000000 log ⁡ n ) (10000000\log n) (10000000logn)

【参考程序】


C:蓝蓝的程序

时间限制: 1 s 1 s 1s 空间限制: 128 M B 128 MB 128MB

【题目描述】

一天,蓝蓝写了一段代码并用它们实现了一些功能.

但是这些程序跑的非常慢,不能在比赛的( 3.5 3.5 3.5 小时/ 5 5 5 小时)中获得结果.

请你帮助他优化这些程序,得到正确的结果.

一共有 10 10 10 个程序。( 10 10 10 个子任务)

因为输入文件太大,所以我们不提供输入文件,只会提供数据规模 n n n 和一个数 s e e d seed seed ,选手可以用 s e e d seed seed 和每道题给你的 g e n 1 ∼ g e n 10. c p p gen1\sim gen10.cpp gen1gen10.cpp 来生成输入数据。

s e e d seed seed 和每道题的 n n n 在选手目录下的 1. t x t ∼ 10. t x t 1.txt\sim 10.txt 1.txt10.txt 中.

蓝蓝提供了他写的程序 1. c p p ∼ 10. c p p 1.cpp\sim 10.cpp 1.cpp10.cpp ,保证它们一次运行只会输出一个数。

蓝蓝的代码不能直接运行,因为它只能在规定时间内解决小范围的数据。

你会发现,在蓝蓝的程序里,输出的数对 998244353 998244353 998244353 取模。

我们要你求出问题的输出。

为了方便选手检查代码,我们会提供答案对 19260817 19260817 19260817 取模的数值,存放在选手目录下的 1. a n s ∼ 10. a n s 1.ans\sim 10.ans 1.ans10.ans 中。

因为某些神秘的原因,我们不使用提交答案式的评测,而是每个测试点里输入一个数 N u m Num Num 表示子任务编号,然后你在程序中输出答案.

【输入格式】

一行一个数 N u m Num Num 表示子任务编号。

【输出格式】

一行一个整数表示这个测试点的答案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值