战争「思维题」
题目描述
内部题不放了
样例
样例输入
2
5
12 34 45 5
10
5
10 15 43 20
5
样例输出
possible
4 100
impossible
思路分析
ps:感谢 yxy
给我讲明白了这道题
太久没水题解了来水一个
(part1)——找普适性规律
- 对于
impossible
的情况很简单,就是每个士兵都可以在最后一击中存活下来,统计一下最小值即可 - 对于
possible
的情况,(40pts) 的直接模拟也很简单,想优化关键在于如何快速地统计伤害 - 为了方便找到一个具有普适性的规律,我们假设在每次枚举小 w 的位置的时候,小 w 后面的人的编号是 (0),然后顺次编号。
- 接下来我们让 (0) 开始攻击,很容易这时候死的士兵全都是奇数,即 (mod%2=1) ,然后继续模拟会发现,每次死的人间距都是相同的,而这恰好是在一些数 (mod) 某个值下才具有的性质,第二轮就是 (mod4=2),第三轮就是 (mod8=4),发现这个规律是个二的指数级增长,恰好对应了每次都死一半减少,也就有 (log) 级轮数
- 所以接下来要做的就是在这 (log) 轮中统计出答案,因为每轮小 w 都只会受到来自他前面那一个活着的士兵的伤害,所以记录这一个编号即可。根据上面的死亡规律,我们其实就可以很方便地处理,如果它的编号是不符合上面的规律的,就记录一次伤害,否则说明他被鲨了,这时候需要更新这个士兵的编号
- 仍然是根据死亡的规律,你会发现每次死都是隔一个人死一个,那么如果一个士兵在本轮死了,那么在上一轮与它相邻的那个活着的士兵就一定活了下来(因为凶手就是他)
(part2)——规律应用
- 上面的规律成立的前提是我们将编号进行了修改,但是如果每次枚举小 w 的位置都修改的话,显然还是会 (TLE) 掉,而且更关键的是要保证让 (1) 号先动手
- 这时候就需要考虑强制性让其满足规律,编号的处理很简单,减去小 w 的位置从而获得相对位置即可,关键是如何让 (1) 先动手,还要不破坏规律。
- 规律好像和需求产生冲突了???貌似两个根本不可能同时满足,然而并不是。既然不是 (1) 先开始鲨,我们就让 (1) 前面的鲨一个"假人",即把这个位置空出来,似有实无,到 (1) 再开始鲨士兵。这时候将序列改成这个样子:
1_2_3_4_5_1 2 3 4 5
,其中_
代表假人。
(早知道这篇题解这么难写就不写了:-))
(Code)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define R register
#define N 1000010
#define ll long long
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const ll inf = 1e15;
int a[N << 2],n,pos;
ll Min,ans;
ll solve(){
ll res = 0;
int cur = 1;
int p = 2*n-2+pos/2;//p代表对小 w 造成伤害的士兵
while(1) {
if(p == pos + 1) break;
cur *= 2;
if((p - pos - 1) % cur == (cur >> 1)) {//p-pos-1即为相对位置,使其编号满足规律成立的条件
p = p - cur / 2;//p变为上一轮的前一个
continue;
}
res += a[p]; //没死就记录下伤害
}
return res;
}
int main(){
int T;T = read();
while(T--) {
Min = inf;
ans = inf;
n = read();
for(R int i = 1;i < n;++i) {
int x = read();
if(x < Min) Min = x;
a[i * 2 - 1] = x;//对序列进行一下修改,没放的位置就是假人
a[2 * n - 2 + i] = x;
}
int F = read();
if(Min > F) {
puts("impossible");
continue;
}
int loc;
for(R int i = 1;i < n;++i) {//枚举小 w 的位置
if(a[i * 2 + 1] > F) continue;
pos = 2 * i;
R int cur = solve();
if(ans > cur || (ans == cur && i == n - 1)) {
ans = cur;
loc = i + 1;
}
}
if(loc == n) loc = 1;
puts("possible");
printf("%d %lld
",loc,ans+F);
}
return 0;
}