题目链接
这场不太难,打起来跟 d i v 3 div\ 3 div 3 一样,会者不难。AB找规律,CD构造,E是暴力,带点数学推理。
A. Nene’s Game
题意:
尼尼发明了一种基于整数递增序列 a 1 , a 2 , … , a k a_1, a_2, \ldots, a_k a1,a2,…,ak 的新游戏。
在这个游戏中,最初 n n n 个玩家排成一排。在这个游戏的每一轮中,都会发生以下情况:
- 妮妮找到第 a 1 a_1 a1 、第 a 2 a_2 a2、 … \ldots … 、第 a k a_k ak 个玩家,他们会同时被踢出游戏。如果一排中的第 i i i 个玩家应该被踢出游戏,但是一排中的玩家少于 i i i 个,那么他们就会被跳过。
一旦在某一轮游戏中没有人被踢出游戏,则宣布所有仍在游戏中的玩家获胜。
例如,假设游戏中有 a = [ 3 , 5 ] a=[3, 5] a=[3,5] 棋手和 n = 5 n=5 n=5 名棋手。让棋手按最初排好的顺序依次命名为棋手 A、棋手 B、 … \ldots … 、棋手 E。那么
- 在第一轮比赛之前,棋手的排列顺序为 ABCDE。妮妮找到了第 3 3 3 个和第 5 5 5 个的棋手。他们在第一轮就被踢出局了。
- 现在棋手们排成ABD。妮妮发现第 3 3 3 位棋手是棋手D,而且一排中没有第 5 5 5 位棋手。因此,第二轮只有棋手 D 被踢出局。
- 在第三轮中,没有人被踢出游戏,所以游戏在这一轮后结束。
- 宣布玩家 A 和 B 获胜。
妮妮还没有决定最初会有多少人参加游戏。妮妮给了你 q q q 个整数 n 1 , n 2 , … , n q n_1, n_2, \ldots, n_q n1,n2,…,nq ,你应该针对每个 1 ≤ i ≤ q 1 \le i \le q 1≤i≤q 分别回答下面的问题:
- 如果最初有 n i n_i ni 个玩家参加游戏,有多少人会被宣布为获胜者?
思路:
机翻翻译的一坨。
手玩一下其实就能发现,假设 a a a 数组里最小的数是 a i a_i ai,那么对大于等于 a i a_i ai 的位置的人都迟早会被踢掉。而较小的得以保留,因此最后最多剩余 a i − 1 a_i-1 ai−1 个获胜者。
code:
#include <iostream>
#include <cstdio>
using namespace std;
int T,n,q;
int minn;
int main(){
cin>>T;
while(T--){
cin>>n>>q;
minn=1e9;
for(int i=1,t;i<=n;i++){
cin>>t;
minn=min(minn,t);
}
minn--;
for(int i=1,t;i<=q;i++){
cin>>t;
cout<<min(t,minn)<<" \n"[i==q];
}
}
return 0;
}
B. Nene and the Card Game
题意:
你和妮妮正在玩纸牌游戏。玩这个游戏使用的是一副有 2 n 2n 2n 张牌的扑克牌。每张牌上都有一个从 1 1 1 到 n n n 的整数,而 1 1 1 到 n n n 的每一个整数都正好出现在 2 2 2 张牌上。此外,游戏中还有一张放置纸牌的桌子(最初桌子是空的)。
游戏开始时,这些 2 n 2n 2n 张牌会在你和妮妮之间分配,这样每位玩家都会得到 n n n 张牌。
之后,你和妮妮各 2 n 2n 2n 轮次,即每人轮流 n n n 次,从你开始。每轮
- 轮到的玩家从手中的牌中选择一张。让 x x x 成为上面的数字。
- 如果桌面上已经有一张整数为 x x x 的牌,则轮到该玩家的玩家会得到 1 1 1 点数(否则,他不会得到任何点数)。之后,他将选中的带有 x x x 整数的牌放在桌上。
请注意,回合是公开进行的:每个玩家在每个时刻都能看到桌面上的所有牌。
妮妮非常聪明,所以她总是以最佳方式选牌,以便在游戏结束时( 2 n 2n 2n 轮之后)最大化自己的分数。如果她有几种最佳走法,她会选择在游戏结束时使你的得分最小的走法。
更正式地说,妮妮总是以最佳方式轮流下棋,以便在对局结束时首先使她的得分最大化,其次使你在对局结束时的得分最小化。
假设纸牌已经分发完毕,而你手中的纸牌上写有整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an ,那么你以最佳方式轮流出牌所能得到的最大分数是多少?
思路:
一个人对手里的一个数字 x x x 的牌,有两种情况:
- 有两张,对方一张都没有。
- 有一张,对方也有一张。
你如果有两张 x x x,对方因为一张都没有,你打出这张牌对方也做不了什么,在这个数字上拿不到分。如果你只有一张 x x x,因为对方也有一张,对方只要也打出 x x x,对方就可以得分,而你由于没有 x x x 了,就得不到分。
手玩一下,其实很容易发现妮妮出牌的策略。如果你出情况 1 1 1 的两张的牌,那妮妮也出两张的牌。如果你出情况 2 2 2 的一张的牌,那妮妮就跟一张一样的。因为两个人手上的牌型是对称的,你有多少情况 12 12 12 的牌,那么妮妮也有同样数量的牌,妮妮一定是有办法执行上面的出牌策略的。
因此使用这种策略打牌的话,你能得到的最大分数就是情况 1 1 1 的牌的对数。
code:
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
const int maxn=2e5+5;
int T,n;
int main(){
cin>>T;
while(T--){
cin>>n;
map<int,int> mp;
for(int i=1,t;i<=n;i++){
cin>>t;
mp[t]++;
}
int ans=0;
for(auto [x,tm]:mp)
ans+=(tm==2);
cout<<ans<<endl;
}
return 0;
}
C. Nene’s Magical Matrix
题意:
魔法女孩妮妮有一个 n × n n\times n n×n 矩阵 a a a ,矩阵中充满了零。矩阵 a a a 第 i i i 行的第 j j j 个元素表示为 a i , j a_{i, j} ai,j 。
她可以对这个矩阵进行以下两种类型的运算:
- 类型 1 1 1 操作:在 1 1 1 和 n n n 之间选择一个整数 i i i 以及从 1 1 1 到 n n n 的整数排列 p 1 , p 2 , … , p n p_1, p_2, \ldots, p_n p1,p2,…,pn 。同时为所有 1 ≤ j ≤ n 1 \le j \le n 1≤j≤n 指定 a i , j : = p j a_{i, j}:=p_j ai,j:=pj 。
- 类型 2 2 2 操作:在 1 1 1 和 n n n 之间选择一个整数 i i i 以及从 1 1 1 到 n n n 的整数的排列 p 1 , p 2 , … , p n p_1, p_2, \ldots, p_n p1,p2,…,pn 。同时为所有 1 ≤ j ≤ n 1 \le j \le n 1≤j≤n 指定 a j , i : = p j a_{j, i}:=p_j aj,i:=pj 。
妮妮想要最大化矩阵 ∑ i = 1 n ∑ j = 1 n a i , j \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}a_{i,j} i=1∑nj=1∑nai,j 中所有数字的和。她要求你找出使这个和最大化的运算方法。由于她不希望进行过多的运算,你应提供一个运算不超过 2 n 2n 2n 的解决方案。
长度为 n n n 的排列是由 n n n 个不同的整数组成的数组,这些整数从 1 1 1 到 n n n 按任意顺序排列。例如, [ 2 , 3 , 1 , 5 , 4 ] [2,3,1,5,4] [2,3,1,5,4] 是一个排列,但 [ 1 , 2 , 2 ] [1,2,2] [1,2,2] 不是一个排列( 2 2 2 在数组中出现了两次), [ 1 , 3 , 4 ] [1,3,4] [1,3,4] 也不是一个排列( n = 3 n=3 n=3 但数组中有 4 4 4 )。
思路:
构造题,赛时莫名其妙地一眼出了构造方法,然后一遍过了,然后莫名其妙的三题上蓝。
题面说的很啰嗦,但是实现的操作很简单,就是有一个排列 数组 p p p,然后把 p p p 覆盖到二维数组 a a a 的某一行或列上,操作最多 2 n 2n 2n 次,问二维数组所有元素和最大的构造方法。
直接说构造方法吧,令 p i = i p_i=i pi=i,然后按顺序,用 p p p 数组覆盖第 n n n 行,覆盖第 n n n 列,然后是第 n − 1 n-1 n−1 行,第 n − 1 n-1 n−1 列,同理一直到第 1 1 1 行,第 1 1 1 列。这样就可以了。构造出来的样子大概如下:
先只看最大的元素,如果我们只用行操作,我们可以让这个元素摆满一行。同理,用列操作我们可以让这个元素摆满一列。那么 2 n 2n 2n 个操作理论上最多可以让这个最大的元素摆满一行的同时也摆满一列。而次大的元素也是占一行和一列,不过要去掉最大的元素占掉的位置。同理第三大的也占一行和一列,再去掉两个最大的格子和两个次大的格子。而上面的构造方法可以达成这个理论上最多的情况,因此上面的构造方式总和是最大的。
输出方案就很简单了,而元素总和 s u m = ∑ i = 1 n ( 2 ∗ i − 1 ) ∗ i = 2 ∑ i = 1 n i 2 − ∑ i = 1 n i = 2 ∗ n ∗ ( n + 1 ) ∗ ( 2 ∗ n + 1 ) 6 − n ∗ ( n + 1 ) 2 = n ∗ ( n + 1 ) ∗ ( 2 ∗ n + 1 ) 3 − n ∗ ( n + 1 ) 2 sum=\sum_{i=1}^n(2*i-1)*i=2\sum_{i=1}^ni^2-\sum_{i=1}^ni=2*\dfrac{n*(n+1)*(2*n+1)}6-\dfrac{n*(n+1)}2=\dfrac{n*(n+1)*(2*n+1)}3-\dfrac{n*(n+1)}2 sum=∑i=1n(2∗i−1)∗i=2∑i=1ni2−∑i=1ni=2∗6n∗(n+1)∗(2∗n+1)−2n∗(n+1)=3n∗(n+1)∗(2∗n+1)−2n∗(n+1)
code:
#include <iostream>
#include <cstdio>
using namespace std;
int T,n;
int main(){
cin.tie(0)->sync_with_stdio(false);
cin>>T;
while(T--){
cin>>n;
cout<<n*(n+1)*(2*n+1)/3-n*(n+1)/2<<" "<<2*n<<endl;
for(int i=n;i>=1;i--){
cout<<"1 "<<i;
for(int j=1;j<=n;j++)
cout<<" "<<j;
cout<<endl;
cout<<"2 "<<i;
for(int j=1;j<=n;j++)
cout<<" "<<j;
cout<<endl;
}
}
return 0;
}
D. Nene and the Mex Operator
题意:
妮妮给了你一个长度为 n n n 的整数数组 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an 。
你可以执行以下操作不超过 5 ⋅ 1 0 5 5\cdot 10^5 5⋅105 次(可能为零):
- 选择 l l l 和 r r r 这样的两个整数 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n ,计算 x x x 为 MEX ( { a l , a l + 1 , … , a r } ) \operatorname{MEX}(\{a_l, a_{l+1}, \ldots, a_r\}) MEX({al,al+1,…,ar}) ,同时设置 a l : = x , a l + 1 : = x , … , a r : = x a_l:=x, a_{l+1}:=x, \ldots, a_r:=x al:=x,al+1:=x,…,ar:=x 。
这里,整数集合 { c 1 , c 2 , … , c k } \{c_1, c_2, \ldots, c_k\} {c1,c2,…,ck} 中的 MEX \operatorname{MEX} MEX 被定义为不出现在集合 c c c 中的最小非负整数 m m m 。
你的目标是最大化数组 a a a 中元素的和。找出最大和,并构建一个操作序列来实现这个和。需要注意的是,你不需要最小化这个序列中的运算次数,你只需要在解决方案中使用不超过 5 ⋅ 1 0 5 5\cdot 10^5 5⋅105 的运算。
思路:
如果序列中的所有数都很小,比如 0 0 0,那么我们肯定用操作给整个序列重新赋值。但是如果有个别位置的值很大,我们 m e x mex mex 值是取不到那么大的,那么这个位置就有可能不操作会更好,而对剩余的两边的序列,又是一个子问题。既然问题本身我们可以找到一个构造方式,那么这个子序列我们也可以找到,也就是可以找到最大值情况,算出来它的值。
显然理论上对一个长为 l e n len len 的序列通过操作可以得到的区间和最大值就是最大的 m e x mex mex 值乘上 l e n len len,也就是 l e n 2 len^2 len2。那么能不能找到一个构造方式来得到它呢?
因为序列原本的值就有可能影响到操作里 m e x mex mex 的结果,所以为了避免这种情况发生,我们先把整个序列置为全 0 0 0。因为 n n n 很小,这里直接暴力枚举来做也未尝不可。假设序列左右端点为 l , r l,r l,r,区间长度为 l e n = r − l + 1 len=r-l+1 len=r−l+1,然后手玩一下,发现操作的序列就是: [ l , l ] , [ l , l + 1 ] , [ l , l ] , [ l , l + 2 ] , [ l , l ] , [ l , l + 1 ] , [ l , l ] , [ l , l + 3 ] … [l,l],[l,l+1],[l,l],[l,l+2],[l,l],[l,l+1],[l,l],[l,l+3]\dots [l,l],[l,l+1],[l,l],[l,l+2],[l,l],[l,l+1],[l,l],[l,l+3]…分析一下其实就是我们把区间 [ l , r ] [l,r] [l,r] 置为全 l e n len len 需要先构造出区间 [ l , r − 1 ] [l,r-1] [l,r−1] 分别为 1 , 2 , 3 , … , l e n − 1 1,2,3,\dots,len-1 1,2,3,…,len−1 的形式(而构造这个形式就需要先把 [ l , r − 1 ] [l,r-1] [l,r−1] 置为全 l e n − 1 len-1 len−1,再把 [ l , r − 2 ] [l,r-2] [l,r−2] 置为全 l e n − 2 len-2 len−2, … \dots …,最后把 [ l , l ] [l,l] [l,l] 置为全 1 1 1),然后再对 [ l , r ] [l,r] [l,r] 用一下操作,这样整个区间就变成了全 l e n len len。
这样,我们只要知道了哪些数不参与操作,其他位置通过操作修改为可以修改到的最大值,我们就能算出最后整个区间的值。因为 n = 18 n=18 n=18 很小很小,我们可以直接暴力枚举每种 b a n & p i c k ban\&pick ban&pick 情况,暴力计算每种情况下的序列的值,记录最大的选取情况即可。
而对需要操作的子序列,我们需要模拟上面提出的构造的过程。这个过程很有递归的感觉,所以用递归来实现,具体来说,我们设置一个函数 p p p,参数传入 l , r l,r l,r,表示将区间 [ l , r ] [l,r] [l,r] 替换为全 l e n = r − l + 1 len=r-l+1 len=r−l+1。
实际实现的时候发现,区间的值是时刻在变化的,如果不同步更新原序列,我们只有第一次检查序列是否全零才是准确的,而在后面操作中,序列我们是知道它每个位置是什么的,因此不需要检查。因此我们还要告诉这个函数:区间是否置为了全 0 0 0,所以再多传入一个参数 i s 0 is0 is0(或者操作时同时暴力地更新序列每个数的改变也是可以的,顶多就是常数大了点)。
code:
#include <iostream>
#include <cstdio>
#include <vector>
#define pii pair<int,int>
using namespace std;
const int maxn=5e5+5;
const int maxk=(1<<18)+5;
int n,a[20];
inline int lowbit(int x){return x&-x;}
inline int sum(int x){return (x==0)?0:x*x;}
int id(int x){
for(int i=0;;i++)
if(x>>i&1)
return i;
}
void pst(int st){
for(int i=0;i<n;i++)
cout<<(st>>i&1);
cout<<endl;
}
int calc(int st){
int l=0,r,ans=0;
for(int x=st,lb;x;x^=lb){
lb=lowbit(x);
r=id(lb);
ans+=a[r]+sum(r-l);
l=r+1;
}
ans+=sum(n-l);
return ans;
}
vector<pii> ans;
void pinit(int l,int r){//区间置为0
bool flag=false;
for(int i=l;i<=r;i++)
if(a[i]==0){
flag=true;
break;
}
ans.push_back(pii(l,r));
if(flag)ans.push_back(pii(l,r));
}
void p(int l,int r,bool is0){//0~x-1 -> x*x
if(!is0)ans.push_back(pii(l,r));//区间置为0
if(l==r){
ans.push_back(pii(l,r));
return;
}
for(int i=r-1;i>=l;i--)
p(l,i,i==r-1);
ans.push_back(pii(l,r));
}
void print(int st){
int l=0,r;
for(int x=st,lb;x;x^=lb){
lb=lowbit(x);
r=id(lb);
if(l<r){
pinit(l,r-1);
p(l,r-1,true);
}
l=r+1;
}
if(l<=n-1){
pinit(l,n-1);
p(l,n-1,true);
}
cout<<ans.size()<<endl;
for(auto [l,r]:ans)
cout<<l+1<<" "<<r+1<<endl;
}
int main(){
cin.tie(0)->sync_with_stdio(false);
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
int s=0,state;
for(int st=0;st<(1<<n);st++){
int t=calc(st);
if(t>s)s=t,state=st;
}
cout<<s<<" ";
print(state);
return 0;
}
E1. Nene vs. Monsters (Easy Version)
题意:
这是问题的简单版本。两个版本的唯一区别在于对 a i a_i ai 的限制。只有两个版本的问题都解决了,才能进行破解。
妮妮正在与位于一个圆圈中的 n n n 个怪物战斗。这些怪物的编号从 1 1 1 到 n n n , 第 i i i 个 ( 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n )怪物当前的能量值是 a i a_i ai 。
由于怪物太强大,妮妮决定使用 “攻击你的邻居” 法术与它们战斗。当妮妮使用这个咒语时,以下行动会按以下顺序依次发生:
- 第 1 1 1 个怪物攻击第 2 2 2 个怪物;
- 2 2 2 nd怪物攻击 3 3 3 rd怪物;
- … \ldots …
- ( n − 1 ) (n-1) (n−1) th怪物攻击 n n n th怪物
- 第 n n n 只怪物攻击第 1 1 1 只怪物
当能量等级为 x x x 的怪物攻击能量等级为 y y y 的怪物时,防守怪物的能量等级变为 max ( 0 , y − x ) \max(0, y-x) max(0,y−x) (攻击怪物的能量等级仍等于 x x x )。
妮妮会使用这个咒语 1 0 100 10^{100} 10100 次,并处理那些自己的能量值仍然不为零的怪物。她希望你能判断出在她使用所述咒语 1 0 100 10^{100} 10100 次之后,哪些怪物的能量值不会为零。
思路:
这题 2500 2500 2500 分?我觉得 2000 2000 2000 都很水,真的很简单。
手玩一下可以发现整个序列很快就会断成一段一段的(中间有 0 0 0 分隔的)序列。原因就是如果第一个数前面是 0 0 0,这个数每轮都会稳定给后面减去能量值,如果第二个数能坚持住,那么它的能量值应该是比较多的,那么第三个数就惨了,它需要一直承受来自第二个数的摧残,导致它很快就会归零。
算一下第三个数最久能坚持多久。那么我们肯定让第一二个数尽可能的小,假设坚持了 t t t 轮,那么第一个数就是 1 1 1,第二个数就是 t t t,那么第三个数受到的总伤害就是 t − 1 + t − 2 + t − 3 + ⋯ + 0 = t ∗ ( t − 1 ) 2 t-1+t-2+t-3+\dots+0=\dfrac{t*(t-1)}2 t−1+t−2+t−3+⋯+0=2t∗(t−1),第三个数的能量是 t 2 t^2 t2 级别的。可以预想到,如果后面还有数的话,数量级应该还会增大,比如第四个数的数量级是 t 3 t^3 t3 级别的等等。
因为序列中的每个数最大也就 2 ∗ 1 0 5 2*10^5 2∗105,所以第三个数最久也就只能坚持 2 ∗ 1 0 5 \sqrt{2*10^5} 2∗105 轮左右。我们可以直接暴力地先跑 2 ∗ 1 0 5 \sqrt{2*10^5} 2∗105 轮,这时剩下的序列最多也就是两个怪挨在一块,这种情况显然第一个怪死不了,而第二个怪一定会死。我们直接统计答案就行了。
code:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=2e5+5;
int T,n,a[maxn];
int main(){
cin>>T;
while(T--){
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
for(int rd=1;rd<=700;rd++){
for(int i=0;i<n;i++){
a[(i+1)%n]=max(0,a[(i+1)%n]-a[i]);
}
}
// for(int i=0;i<n;i++)cout<<a[i]<<" \n"[i==n-1];
for(int i=0;i<n;i++)
if(a[i]>0 && a[(i+1)%n]>0)
a[(i+1)%n]=0;
vector<int> ans;
for(int i=0;i<n;i++)
if(a[i]>0)
ans.push_back(i+1);
cout<<ans.size()<<endl;
for(auto x:ans)
cout<<x<<" ";
cout<<endl;
}
return 0;
}
E2. Nene vs. Monsters (Hard Version)
题意:
这是问题的困难版本。两个版本的唯一区别在于对 a i a_i ai 的限制。只有两个版本的问题都解决了,才能进行破解。
妮妮正在与位于一个圆圈中的 n n n 个怪物战斗。这些怪物的编号从 1 1 1 到 n n n , i i i -th ( 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n )怪物当前的能量值是 a i a_i ai 。
由于怪物太强大,妮妮决定使用 "攻击你的邻居 "法术与它们战斗。当妮妮使用这个咒语时,以下行动会按以下顺序逐一发生***:
- 第 1 1 1 只怪物攻击第 2 2 2 只怪物;
- 第 2 2 2 只怪物攻击第 3 3 3 只怪物;
- … \ldots …
- 第 ( n − 1 ) (n-1) (n−1) 只怪物攻击第 n n n 只怪物
- 第 n n n 只怪物攻击第 1 1 1 只怪物
当能量等级为 x x x 的怪物攻击能量等级为 y y y 的怪物时,防守怪物的能量等级变为 max ( 0 , y − x ) \max(0, y-x) max(0,y−x) (攻击怪物的能量等级仍然等于 x x x )。
妮妮会使用这个咒语 1 0 100 10^{100} 10100 次,并对付那些自己的能量值仍然不为零的怪物。她希望你能判断出在她使用所述咒语 1 0 100 10^{100} 10100 次之后,哪些怪物的能量值不会为零。
思路:
有了 E 1 E1 E1 的思路, E 2 E2 E2 的思路也就很好想了,假设循环的轮数是 t t t,那么连续的几个存活的怪物上,第四只怪物需要承受 t 3 t^3 t3 级别的伤害,而怪物血量的上限也就是 1 0 9 10^9 109,因此如果轮数进行了 1 0 9 3 \sqrt[3]{10^9} 3109 级别次,那就不可能出现四只及以上的怪物连续出现的情况。
对剩余的单个怪物,两个怪物连续出现,三个怪物连续出现的情况,我们直接分情况处理一下最终状态就行了。前两个情况很显然,现在讨论第三个情况:
三个怪物连续出现的话,第一个怪一定存活,第二个怪一定死亡,如果第三个怪能坚持到第二个怪死掉,那么它也可以存活。假设第一个怪的能量为 x 1 x_1 x1,第二个怪的能力为 x 2 x_2 x2,第三个怪的能力为 x 3 x_3 x3,设 d = ⌊ x 2 x 1 ⌋ d=\left\lfloor\dfrac{x_2}{x_1}\right\rfloor d=⌊x1x2⌋,这时第三个怪承受的总伤害为 = x 2 − x 1 + x 2 − 2 x 1 + ⋯ + x 2 − d ∗ x 1 = d ∗ x 2 − d ∗ ( d + 1 ) 2 ∗ x 1 =x_2-x_1+x_2-2x_1+\dots+x_2-d*x_1=d*x_2-\dfrac{d*(d+1)}{2}*x_1 =x2−x1+x2−2x1+⋯+x2−d∗x1=d∗x2−2d∗(d+1)∗x1,和 x 3 x_3 x3 比较一下就知道它死不死了。
不过需要注意一点是有可能出现 x n , x 1 , x 2 ≠ 0 x_n,x_1,x_2\not=0 xn,x1,x2=0 的情况,但是我们从第一个数开始枚举,就有可能把它错认为是第二种情况。而且由于攻击顺序不是 x n → x 1 → x 2 x_n\rightarrow x_1\rightarrow x_2 xn→x1→x2,而是 x 1 → x 2 , x n → x 1 x_1\rightarrow x_2,x_n\rightarrow x_1 x1→x2,xn→x1,因此第三个怪承受的总伤害变为了 x 2 + x 2 − x 1 + ⋯ + x 2 − d ∗ x 1 = ( d + 1 ) ∗ x 2 − d ∗ ( d + 1 ) 2 ∗ x 1 x_2+x_2-x_1+\dots+x_2-d*x_1=(d+1)*x_2-\dfrac{d*(d+1)}{2}*x_1 x2+x2−x1+⋯+x2−d∗x1=(d+1)∗x2−2d∗(d+1)∗x1,需要特判一下。
code:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
int T,n,a[maxn];
int main(){
cin.tie(0)->sync_with_stdio(false);
cin>>T;
while(T--){
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
for(int cnt=1,nxt;cnt<=1820;cnt++){//多试几个数,或者直接写个二分算都行
for(int i=0;i<n;i++){
nxt=(i+1)%n;
a[nxt]=max(0,a[nxt]-a[i]);
}
}
// for(int i=0;i<n;i++)cout<<a[i]<<" \n"[i==n-1];
int st;
for(st=0;a[st];st++);
for(int i=0,i1,i2,i3,d;i<n;i++){
i1=i%n;
i2=(i+1)%n;
i3=(i+2)%n;
if(a[i1]>0 && a[i2]>0 && a[i3]>0){
d=a[i2]/a[i1];
ll dt=(2ll*a[i2]-1ll*(d+1)*a[i1])*d/2;
if(i1>i2)dt+=a[i2];
a[i3]=max(0ll,(ll)a[i3]-dt);
a[i2]=0;
}
}
for(int i=0,i1,i2,d;i<n;i++){
i1=i%n;
i2=(i+1)%n;
if(a[i1]>0 && a[i2]>0)a[i2]=0;
}
vector<int> ans;
for(int i=0;i<n;i++)
if(a[i]>0)
ans.push_back(i+1);
cout<<ans.size()<<"\n";
for(auto x:ans)
cout<<x<<" ";
cout<<"\n";
}
return 0;
}