P7078 [CSP-S2020] 贪吃蛇
这题好啊
题目传送门
看到题之后觉得有点像砍蚯蚓的那道题
看看题目
可以证明,若一条蛇在吃完之后不是最弱的那一条蛇,那么他一定会选择吃,证明如下
设蛇长为 \(a_{1, \dots ,n}\) 且依次递增,那么很明显的
因为
$$a_x>a_y>a_n>a_m$$
$$ a_x-a_m>a_y-a_n$$
也就是说,这条蛇吃掉后面的蛇之后产生的蛇永远都不会是最小的哪一个
而如果一条蛇在吃完了之后是最弱的蛇,那么他为了保命,会看自己吃了之后会不会被吃,也就是看下一条蛇的决策
而下一条蛇的决策过程和这一条蛇也是一样的,这种情况就可以 递归的来判断.
还要补充两点
- 如何维护最强最弱的蛇?
可以用平衡树,set 等,但这种做法会带上一只 \(log\) ,所以一般会T
正确的做法是像砍蚯蚓的那道题一样,用双端队列维护
像这道题,用两个双端队列,一个维护所有原来的蛇,另一个维护吃完蛇后产生的蛇.
对于第二个双端队列的单调性的证明,就是我们一开始证的东西, 后吃的蛇所产生的蛇的长度一定 比 先吃的产生的长度小.
- 处理一次第二种情况后就可以立即停止了
这是因为第二种情况若吃了,就说明下一条蛇不会吃,结束
若没吃,也结束
上代码!
点击查看代码
#include <bits/stdc++.h>
using namespace std;
struct snack
{int len, num;friend bool operator<(const snack &a, const snack &b){if (a.len == b.len)return a.num < b.num;return a.len < b.len;}friend bool operator>(const snack &a, const snack &b){if (a.len == b.len)return a.num > b.num;return a.len > b.len;}friend snack operator-(const snack &a, const snack &b){snack tmp;tmp.len = a.len - b.len;tmp.num = a.num;return tmp;}void clear(){this->len = 0;this->num = 0;}
};
snack snac[10000100]; //蛇群们
int t, n, k;
deque<snack> q1, q2; //两个双端队列来维护
void clean_up()
{q1.clear();q2.clear();
}
snack get_min(bool delt)
{snack ll;if (q1.empty() && q2.empty()){snack insid;insid.len = -1;insid.num = -1;return insid;}else if (q1.empty()){ll = q2.front();if (delt)q2.pop_front();return ll;}else if (q2.empty()){ll = q1.front();if (delt)q1.pop_front();return ll;}else{if (q1.front() < q2.front()){ll = q1.front();if (delt)q1.pop_front();return ll;}else{ll = q2.front();if (delt)q2.pop_front();return ll;}}
}
snack get_max(bool del)
{snack ll;if (q1.empty() && q2.empty()){snack insid;insid.len = -1;insid.num = -1;return insid;}else if (q1.empty()){ll = q2.back();if (del)q2.pop_back();return ll;}else if (q2.empty()){ll = q1.back();if (del)q1.pop_back();return ll;}else{if (q1.back() > q2.back()){ll = q1.back();if (del)q1.pop_back();return ll;}else{ll = q2.back();if (del)q2.pop_back();return ll;}}
}
int lef = n, llef;
bool fight()
{snack min_, max_;if (llef == 2){return 1;}min_ = get_min(1);max_ = get_max(1);if (max_ - min_ > get_min(0)){return 1;}else{llef--;q2.push_front(max_-min_);return !fight();}
}
int solve()
{for (int ww = 1; ww <= n; ww++){q1.push_back(snac[ww]);}// case1;lef = n;snack new_, tmp;while (1){if (lef == 2){lef--;}if (lef == 1){break;}new_ = get_min(1);tmp = get_max(1);// cout<<"get "<<new_.len<<" "<<tmp.len<<endl;// cout<<(tmp-new_).len<<" 对 " <<get_min(0).len<<endl;if (tmp - new_ > get_min(0)){q2.push_front(tmp - new_);lef--;}else{q2.push_front(tmp - new_);break;}}if (lef == 1){return lef;}// case2,判断完直接就结束了llef = lef;if (!fight()){lef--;}return lef;
}
int main()
{freopen("P7078_5.in","r",stdin);ios::sync_with_stdio(false);cin >> t;cin >> n;for (int yy = 1; yy <= n; yy++){cin >> snac[yy].len;snac[yy].num = yy;}cout << solve() << "\n";for (int ww = 2; ww <= t; ww++){clean_up();cin >> k;int a, b;for (int ww = 1; ww <= k; ww++){cin >> a >> b;snac[a].len = b;}cout << solve() << "\n";}return 0;
}