zoukankan      html  css  js  c++  java
  • [CSP-S2020] 贪吃蛇

    前言

    喜欢思维题,更喜欢做不出来但看了题解直呼妙的思维题。

    但是讨厌考试时做不出来的思维题。/youl

    警告:本文语言臭长,如果不想看直接 Ctrl+W 走人即可。

    但是如果你有足够的耐心慢慢看,我也有足够的信心把你讲懂。

    题目

    洛谷

    讲解

    这种题显然要先挖掘性质。

    • 性质一:如果一条蛇吃了之后不是最弱蛇,它一定吃。

    证明的话稍微分类讨论一下就好了:

    1. 如果最强蛇吃了之后还是最强蛇,不吃白不吃。
    2. 最强蛇吃了之后不是最强蛇,那么次强蛇上位,次强蛇如果选择不吃,安全;次强蛇如果吃,吃的一定是次弱蛇,吃了之后一定比最强蛇吃了最弱蛇还要弱,在之后的操作中如果次强蛇会被吃,那么现在这次操作它一定会选择不吃,依然安全。

    现在问题就变成最强蛇吃了最弱蛇之后自己是最弱蛇的情况了。

    为了方便,我们将所有蛇按实力动态排序为 ([1,n])

    现在我们假设 (n) 吃了 (1),原来的 (n) 就变成了 (1) (动态更新排名),考虑 (n-1) 要不要吃 (1),如果 (n-1) 吃了 (1) 之后不是最弱蛇或者只剩一条蛇,它就会选择吃,否则我们需要假设 (n-1)(1),然后看 (n-2) 的抉择...

    可以发现最后一定可以找到一条必吃的蛇,然后判断它和最初始的 (n) 的奇偶性是否相同即可判断最初的 (n) 是否选择吃。

    用 set 模拟这个过程可以做到 (O(Tnlog_2n))(70pts) 到账。

    显然正解应该是 (O(Tn)) 的,题目限制提示我们应该会用到单调性,由于第二个过程可以 (O(n)) 实现,所以我们只需考虑优化第一个过程。

    大力思考第一个过程中有什么性质。

    • 性质二:后吃的蛇一定比先吃的蛇弱。

    现在我们用两个双端队列存储这些蛇,第一个存的是初始没用过餐的蛇,第二个存的是用过餐的蛇,队首弱,队尾强。

    我们每次要做的是从两个队列的队尾取出一个最强的,从队列一的队首取出最弱的(由性质一+过程一可得出队列二中一定不存在最弱的),吃掉之后与次弱比较,强于次弱则直接加入队列二的队首。

    在性质一的证明 2. 中我们粗略说明了最强蛇和次强蛇都吃,次强蛇一定比最强蛇弱的结论。

    所以这么吃下去,用过餐的蛇一定也是单调的,但是这只说明了队列一中的蛇连续吃满足性质二,有没有可能最强蛇在队列二中,且吃了之后比当前队列二队首强呢?

    不可能。

    我们考虑当前队列二队首 (h_2) 是怎么进来的:

    (h_2) 还没进来的时候,它一定是队列一队尾,它和队列二队尾 (t_2) 比较之后发现自己更强,于是吃掉了那个时候的最弱蛇 (s_1) ,可以说明没吃的时候 (h_2)(t_2)

    (h_2) 进来之后,最弱蛇 (s_1) 已经由之前的次弱蛇 (s_2) 顶替,由 (h_2) 强于 (t_2)(s_1) 弱于 (s_2) 可知此时如果 (t_2) 吃掉这条 (s_2) ,一定比 (h_2) 吃掉 (s_1) 更弱,所以吃掉之后可以直接放到队列二队首,性质二完全正确!

    在过程一结束之后只需要对这两个队列进行归并排序即可做到 (O(n))

    总时间复杂度 (O(Tn))

    代码

    其实没有想象中那么难打
    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std;
    
    typedef long long LL;
    const int MAXN = 1000005;
    const int INF = 0x3f3f3f3f;
    int n;
    int a[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1; char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    struct node
    {
    	int val,ID;
    	bool operator < (const node &px)const{
    		if(val^px.val) return val < px.val;
    		return ID < px.ID;
    	}
    	node operator - (const node &C)const{
    		return node{val-C.val,ID};
    	}
    }les[MAXN];
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	int T = Read();
    	for(int cas = 1;cas <= T;++ cas) 
    	{
    		if(cas == 1)
    		{
    			n = Read();
    			for(int i = 1;i <= n;++ i) a[i] = Read();
    		}
    		else 
    		{
    			int k = Read();
    			for(int i = 1,pos;i <= k;++ i) pos = Read(),a[pos] = Read();
    		}
    		deque<node> q1,q2;
    		for(int i = 1;i <= n;++ i) q1.push_back(node{a[i],i});
    		int q1len,q2len;
    		while((q1len = q1.size()) + (q2len = q2.size()) > 2)//最小的一定是q1的头,所以执行循环时q1永不空, 
    		{
    			if(q2len && q1.back() < q2.back())//q2大! 
    			{
    				node ne = q2.back() - q1.front(),se = node{INF,n+1};//new & second 
    				if(q1len > 1 && q1[1] < se) se = q1[1];
    				if(q2len && q2[0] < se) se = q2[0];
    				if(se < ne) q2.push_front(ne),q1.pop_front(),q2.pop_back();
    				else break;
    			}
    			else//复读机嘛 
    			{
    				node ne = q1.back() - q1.front(),se = node{INF,n+1};//new & second 
    				if(q1len > 1 && q1[1] < se) se = q1[1];
    				if(q2len && q2[0] < se) se = q2[0];
    				if(se < ne) q2.push_front(ne),q1.pop_front(),q1.pop_back();
    				else break;
    			}
    		}
    		if(q1len+q2len <= 2)
    		{
    			Put(1,'
    ');
    			continue;
    		}
    		//归并 
    		int tot = 0;
    		while(!q1.empty() && !q2.empty())
    			if(q1.front() < q2.front()) les[++tot] = q1.front(),q1.pop_front();
    			else les[++tot] = q2.front(),q2.pop_front();
    		while(!q1.empty()) les[++tot] = q1.front(),q1.pop_front();
    		while(!q2.empty()) les[++tot] = q2.front(),q2.pop_front();
    		int ans = tot;
    		while(tot > 2) 
    		{
    			if(les[tot] - les[1] < les[2]) les[1] = les[tot] - les[1],--tot;
    			else break;
    		}
    		if((tot & 1) == (ans & 1)) Put(ans-1,'
    ');
    		else Put(ans,'
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    CSS 之 @media
    How to fix “Duplicate sources.list entry …” issue
    shell脚本加不加export的区别
    过滤部分错误信息,不输出到stderr
    /dev/null 2>&1 解释(转)
    crontab与环境变量
    PHP实现斐波那契数列非递归方法
    有反斜杠时候,CakePHP往pgsql插入数据异常
    PHP输出图片文件,实现浏览器缓存机制
    sudo: unable to resolve host XXX 解决方法
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15440593.html
Copyright © 2011-2022 走看看