A

问题陈述(DeepL翻译)

给你一个长度为 NN 的序列 A=(A1,A2,,AN)A = (A_1, A_2, \cdots , A_N)
然后,给出一个介于 11NN 之间的整数 XX
输出值 AXA_X

思路:

存储AA数组,输出AXA_X即可。

Code

1
2
3
4
5
6
7
8
int n,x;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
cin>>x;
cout<<a[x];

B

问题陈述(DeepL翻译)

给你 NN 序列 A1,A2,,ANA_1, A_2, \ldots, A_N
序列 AiA_i 的长度为 LiL_iAi=(Ai,1,Ai,2,,Ai,Li)A_i = (A_{i,1}, A_{i,2}, \ldots, A_{i,L_i})
之后,给出整数 XXYY 。输出 AX,YA_{X,Y} 的值。

  • LiL_i 的总和最多为 2×1052 \times 10^5

思路:

与A题一样存储AA数组,输出AX,YA_{X,Y},由于入方式比较怪异,不过限制了总输入量,可以完全存入。
需要开一个二维数组,但是开NLmaxN*L_{max}的大小会超出空间上限,因此采用vector存数据。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cin>>n;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
a[i].assign(x+2,0);
for(int j=1;j<=x;j++)
{
int tem;
cin>>tem;
a[i][j]=tem;
}
}
cin>>x>>y;
cout<<a[x][y];

C

问题陈述

给定整数 NNKK 以及 NN 个整数序列 A1,A2,,ANA_1, A_2, \ldots, A_N 和一个长度为 NN 的整数序列 C=(C1,C2,,CN)C = (C_1, C_2, \ldots, C_N) 。整数序列 AiA_i 的长度为 LiL_iAi=(Ai,1,Ai,2,,Ai,Li)A_i = (A_{i,1}, A_{i,2}, \ldots, A_{i,L_i}) 。可以保证 1Ki=1NCiLi\displaystyle 1 \le K \le \sum_{i=1}^N C_iL_i
用下面的步骤从 AACC 构造一个整数序列 B=(B1,B2,,Bi=1NCiLi)B = (B_1, B_2, \ldots, B_{\sum_{i=1}^N C_iL_i}) .

  • BB 是长度为 00 的整数序列。
  • 按此顺序对 i=1,2,,Ni = 1, 2, \ldots, N 进行如下操作:
    • 执行 CiC_i 次:将 AiA_i 追加到 BB 的末尾。

求出 BKB_K 的值。
部分数据范围

  • i=1NLi2×105\displaystyle \sum_{i=1}^N L_i \le 2 \times 10^5
  • 1Ci1091 \le C_i \le 10^9
  • 1Ki=1NCiLi\displaystyle 1 \le K \le \sum_{i=1}^N C_iL_i

分析:

首先要了解题意,与B题一样给出了一个二维数组AA,要我们构造出一个数组BB,构造方式为顺序将AA的第ii行复制CiC_i次,输出BKB_K
考虑数据范围 ,这个K可能很大,最大为多大呢?令所有的Ci=109C_i=10^9,提出CC后,Kmax=109(2105)=21014K_{max}=10^9*(2*10^5)=2*10^{14},必须使用long long存储。

思路:

使用一个cntcnt计数,作为目前BB的下标,对于每一行AiA_i,如果cnt+CiLi<Kcnt+C_i*L_i<K时,说明答案BKB_K不在这一行,更新cntcntcnt+CiLicnt+C_i*L_i,进入下一行。
cnt+CiLi>=Kcnt+C_i*L_i>=K时,说明答案已经出现。我们要找到第KK个,令K=cnt+XK=cnt+X,将AiA_i行复制CiC_i次后的数组中的第X个即为答案,由于是循环数组,令X=kLi+rX=k*L_i+r,答案即为Ai,rA_{i,r},注意到当r=0r=0时,实际上答案为Ai,LiA_{i,L_i},特殊处理一下即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
cin>>N>>K;
for(int i=1;i<=N;i++)
{
int L;
cin>>L;
a[i].assign(L+1,0);
a[i][0]=L;
for(int j=1;j<=L;j++)
{
int tem;
cin>>tem;
a[i][j]=tem;
}
}
for(int i=1;i<=N;i++)
{
cin>>C[i];
}
int cnt=0;
for(int i=1;i<=N;i++)
{
if(cnt+a[i][0]*C[i]<K)
{
cnt+=a[i][0]*C[i];
continue;
}
else
{
int tem=(K-cnt)%a[i][0];
if(tem==0)tem=a[i][0];
cout<<a[i][tem];
break;
}
}

D

问题陈述

给你一个长度为 NN 的序列 A=(A1,A2,,AN)A = (A_1, A_2, \ldots, A_N) 和一个整数 KK
您可以在 00KK 之间(包括首尾两次)进行以下运算。

  • 选择一个满足 1iN1 \le i \le N 的整数 ii ,将 ii 加到 AiA_i 中。

求运算后序列 min1iNAi\displaystyle \min_{1 \le i \le N} A_i 的最大可能值。

分析:

题目要求你进行至多KK次操作,将AA数组的第ii位增加ii,即令Ai=Ai+iA_i=A_i+i,使AA中的最小值变得最大

思路:

首先考虑进行KK次肯定是更优的,如果进行K1K-1次,那就应该对目前AA的最小值再进行一次操作从而得出更优答案。
显而易见的贪心策略,每次寻找AA的最小值并且增加ii,最后输出AA的最小值。如果采用数组存储,则时间复杂度为O(NK)O(N*K)。如果采用优先队列存储二元组(Ai,i)(A_i,i),取出存入K次,时间复杂度为O(KlogN)O(K\log N)。贪心策略的复杂度显然是无法满足该题的。
对于这种最大化最小值的题目,合格的ACMer应该首先想到二分答案
考虑其答案是否具有单调性,设XX,在进行KK次操作后,满足任意ii,都有Ai>=XA_i>=X,那么XX为可能的答案。假设真正的答案为ansans,当X<ansX<ans时,该条件均成立,而当X>ansX>ans时,条件不成立。因此可以采用二分答案求解。
check函数写法:
对于一个XX,我们遍历数组AA,每次将该AiA_i增大至大于等于XX,记录消耗的次数,如果所需次数大于KK,则XX取大了,我们需要选择更小的XX

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
bool check(int x)
{
int cnt=K;
for(int i=1;i<=N;i++)
{
int tem=a[i].first;
if(a[i].first>=x)return 1;
int q=(x-a[i].first)/a[i].second;
tem+=q*a[i].second;
if(tem<x)q++;
cnt-=q;
if(cnt<0)return 0;
}
return 1;
}

void sol()
{
cin>>N>>K;
for(int i=1;i<=N;i++)
{
int x;
cin>>x;
pair<int,int>tem=make_pair(x,i);
a[i]=tem;
}
sort(a+1,a+N+1);
int l=a[1].first,r=2e18;
int ans=l;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid))
{
ans=mid;
l=mid;
}
else
{
r=mid-1;
}
}
cout<<ans;
}

好了,赛时写到这里就结束了,E题最终没有通过

补题

E

问题陈述

NN 个单元格排列成一行。从左边 (1iN)(1 \le i \le N) 起的第 ii 个单元格称为单元格 ii
一共有 MM 块布。铺布 ii (1iM)(1 \le i \le M) 覆盖了 LiL_iRiR_i 单元格。
回答 QQ 个问题。对于 qq -th 查询 (1qQ)(1 \le q \le Q) ,给出了整数 SqS_qTqT_q ,因此请回答下面的问题。

  • 确定是否可能从 MM 块布中恰好选择两块布并铺好,从而满足以下条件。
    • SqS_qTqT_q 单元格至少有一块布覆盖,其他单元格没有任何布覆盖。

分析:

给了MM个区间[Li,Ri][L_i,R_i],范围为NN,有QQ次询问,让你选择2个区间刚好覆盖[S,T][S,T]

思路:

对于每一个询问区间[S,T][S,T],我们先寻找一个区间[Li,Ri][L_i,R_i],记为区间ii,其中Li=SL_i=S,否则无法覆盖,输出No
再考虑第二个区间[Lj,Rj][L_j,R_j],记为区间jj:我们要覆盖区间[S,T][S,T],则必然有Ri=TR_i=TLi=TL_i=T

  • Ri=TR_i=T时,那么区间jj可为[S,T][S,T]的任意子区间。
  • Rj=TR_j=T时,需要两个区间覆盖整个[S,T][S,T],则要满足Ri+1<=LjR_i+1<=L_j

本题的有很多的细节需要处理,比如需要保证第二个区间和第一个区间不是同一个区间,需要预处理判断是否存在第二个区间包含在[S,T][S,T]。理论的时间复杂度为O(Q)O(Q)
Ps:本体的数据比较水,遍历第二个区间也能过,最坏复杂度为O(QN)O(Q*N)比如我的代码就这么过了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
int N,M,Q;
vector<pair<int,int> >l[maxn],r[maxn];

bool check(int s,int t)
{
if(l[s].empty()||r[t].empty())return 0;
auto ans1=lower_bound(l[s].begin(),l[s].end(),make_pair(-t,1ll));
if(ans1==l[s].end())return 0;
if(ans1->first==-t)
{
for(int i=s;i<=t;i++)
{
if(l[i].empty())continue;
if(l[i].back().first>=-t&&l[i].back().second!=ans1->second)
{
return 1;
}
}
return 0;
}
else
{
auto ans2=lower_bound(r[t].begin(),r[t].end(),make_pair(s,1ll));
if(ans2==r[t].end())return 0;
if(ans2->first<=(1-ans1->first))return 1;
else return 0;
}
}

void sol()
{
cin>>N>>M;
for(int i=1;i<=M;i++)
{
int tl,tr;
cin>>tl>>tr;
l[tl].push_back(make_pair(-tr,i));
r[tr].push_back(make_pair(tl,i));
}
for(int i=1;i<=N;i++)
{
if(l[i].size())
{
sort(l[i].begin(),l[i].end());
}
if(r[i].size())
{
sort(r[i].begin(),r[i].end());
}
}
cin>>Q;
while(Q--)
{
int s,t;
cin>>s>>t;
if(check(s,t))
{
cout<<"Yes\n";
}
else
{
cout<<"No\n";
}
}
}

G

问题陈述

NN 个苹果落在一条数线上。苹果 ii 在时间 TiT_i 落在坐标 XiX_i 处。
你想在数线上放置一些机器人来收集所有的 NN 苹果。机器人可以放置在任意坐标上。
每个机器人从时间 00 开始运行,并能以最多 11 的速度沿数字线自由移动。多个机器人可以同时占据同一坐标。当且仅当每个机器人在时间 TiT_i 时位于坐标 XiX_i 时,它才能收集苹果 ii
求收集所有苹果所需的最少机器人数量。

分析:

给定了NN对数据TiT_iXiX_i,苹果会在TiT_i时出现在XiX_i处,每个机器人可以以1的速度在一维坐标上运动,问需要多少机器人才能获得所有苹果。

思路:

赛时根据DD题的启发,而答案确实是单调的,所以是有可能实现的,因此考虑了贪心策略,但是根据正解是最小链覆盖问题,好像真的写不出来吧。。?
假设一个机器人已经接住了第ii个苹果,想要接第jj个苹果,且Tj>TiT_j>T_i那么需要满足的条件为 TjTi>=XjXiT_j-T_i>=|X_j-X_i| ,拆开绝对值并移项后为TjXj>=TiXiT_j-X_j>=T_i-X_i并且Tj+Xj>=Ti+XiT_j+X_j>=T_i+X_i。那么我们定义二元组A(TXT-X,T+XT+X),机器人收集了AiA_i的苹果后,想要收集AjA_j苹果的话,就需要满足Ai<=AjA_i<=A_j。那么这个机器人可以收集最多多少个苹果呢?如果只有一个机器人,这就是一个典型的求AA最长-不下降-子序列问题(LIS),而我们要求的是最少多少个机器人,那就变成了AA最小链覆盖问题。由Dilworth定理知道最小链覆盖数 = 最长反链。因此本题就是求最长反链长度,也就是求AA最长下降子序列。时间复杂度就是LIS的复杂度O(NlogN)O(N\log N)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int N;
struct S
{
int t,x;
}a[maxn];
vector<int>ans;

bool cmp(S x,S y)
{
if(x.t==y.t)return x.x<y.x;
return x.t<y.t;
}

void sol()
{
cin>>N;
for(int i=1;i<=N;i++)
{
int t,x;
cin>>t>>x;
a[i].t=t+x;
a[i].x=t-x;
}
sort(a+1,a+1+N,cmp);
for(int i=1;i<=N;i++)
{
int pos=lower_bound(ans.begin(),ans.end(),-a[i].x)-ans.begin();
if(pos==ans.size())
{
ans.push_back(-a[i].x);
}
else ans[pos]=-a[i].x;
}
cout<<ans.size();
}

感悟

终于写完了!第一次写题解花费了非常长的时间,感觉应该写的过于详细…?可能吧
也是终于体会到了写一篇题解有多么复杂,我目前而言是不愿意用太多术语写题解的,但是这也导致表达思路的话会十分复杂,可能以后就不会再这么写也没什么时间这么写了。
希望我能继续坚持下去,加油!