• 战争「思维题」


    战争「思维题」

    题目描述

    内部题不放了

    样例

    样例输入

    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;
    }
    
  • 相关阅读:
    给Windows组件添加图标
    C#文件和文件夹操作
    WinForm TreeView 右键菜单
    VC++ New 操作符
    Ext与Jquery的整合
    PowerDesign报表操作
    SQLServer自动建表存储过程
    Visual Studio 2008简体中文正式版下载地址
    WinForm遍历控件
    发布时用直接用源文件部署
  • 原文地址:https://www.cnblogs.com/hhhhalo/p/13713415.html
Copyright © 2020-2023  润新知