xjtuoj 1151:把你挂在地灵殿门口当装饰品!

题目来源

恋恋,嘿嘿,恋恋我要挂在地灵殿门口

题意看起来很无意识,实际上这个序列是由一段\(A\)类罪袋+\(BC\)混杂罪袋组合而成,因此可以先找出序列开头的一段连续上升子序列作为\(max(k-m)\)(也就是\(PosA\)
接着考虑\(C\)类罪袋,容易看出\(C\)类罪袋必定是从\(N\)开始递减的一段子序列(倒序了),因此找到\(C\)类罪袋的结束点\(PosC\)
最后是\(B\)类罪袋,已知了\(PosC\)那么只要倒序往前找比\(PosC\)小的值即可,并且更新比较值。当找到大于比较值得数的时候先进行判断,如果此时循环到的序列的\(i\)\(PosA\)大,那么说明出现了\(ABC\)罪袋都无法取到的值,反之说明找到了\(B\)罪袋的结束点\(PosB\)。接着跳出循环。
最后得到了\(PosB\)\(PosC\),两者取最小值即是\(min(k-m)\)

代码
#include<bits/stdc++.h>
#define F(i,n,m) for(int i=n;i<m;i++)
#define f(i,n,m) for(int i=n;i>m;i--)
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
inline int read() {
	int num = 0;
	char c;
	bool flag = false;
	while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
	if (c == '-') flag = true;
	else
		num = c - '0';
	while (isdigit(c = getchar()))
		num = num * 10 + c - '0';
	return (flag ? -1 : 1) * num;
}
int zd[200005];
int main() {
	std::ios::sync_with_stdio(false);
	int n;
	n=read();
	int posA,posB,posC;
	posA=posB=posC=0;
	F(i,0,n)  zd[i]=read();
	F(i,0,n-1) if(zd[i]>zd[i+1]) {
		posA=i;
		break;
	}
	int ls=n;
	f(i,n-1,-1) if(zd[i]==ls) {
		posC=i;
		ls--;
	}
	int flag=1;
	f(i,n-1,-1) {
		if(zd[i]<zd[posC]) {
			if(zd[i]<=ls) {
				posB=i;
				ls=zd[i];
			} else {
				if(i>posA)flag=0;
				break;
			}
		}
	}
	if(flag) cout<<min(posB,posC)<<" "<<posA+1;
	else cout<<-1<<" "<<-1;
	return 0;
}
感觉可以被叉掉?看了下对应排行榜的一些AC代码没过特殊样例但过了评测

咦所以怎么看起来是模拟不是贪心或者二分(

posted @ 2022-04-23 06:00  FPICZEIT  阅读(65)  评论(0编辑  收藏  举报