1. 程式人生 > >牛客國慶集訓派對Day1: K. Tengen Toppa Gurren Lagann(貪心)

牛客國慶集訓派對Day1: K. Tengen Toppa Gurren Lagann(貪心)

K. Tengen Toppa Gurren Lagann

時間限制:C/C++ 1秒,其他語言2秒
空間限制:C/C++ 1048576K,其他語言2097152K
64bit IO Format: %lld

題目描述

Kamina正在為從世界各地集結而來的Ganmen(一種戰鬥機器人)軍團整隊。
Kamina麾下一共有 n 臺Ganmen,每臺Ganmen有一個互不相同的編號,編號的範圍是 [1, n] 。Kamina命令 n 臺Ganmen排成了一列,並決定委託Simon將這個序列分成 k 段,每段是一個小組。Kamina希望在每個小組內部按照編號升序排序之後,整個序列是遞增的,為了增大成功率,他允許Simon在分好段之後任意地交換其中的兩段,這個操作不是必須的,且最多進行一次。
現在,Simon希望知道他是否有可能完成這個任務。

輸入描述:

第一行包括兩個正整數 n (1 ≤ n ≤ 1000000)和 k (1 ≤ k ≤ n)。
第二行包括 n 個正整數 a1, a2, ..., an,資料保證 a 是一個1至n的排列。ai表示第i個Ganmen的編號。

輸出描述:

一行,如果有解輸出“Yes”,無解輸出“Poor Simon”,不包括引號。

輸入

10 3
10 9 8 7 5 6 4 3 2 1

輸出

Yes

輸入保證是個全排列

設L[k]表示前k個元素的最大值,F[l,r]表示區間[l,r]最多能分成多少段滿足每一段排完序後整體升序(不能交換),sort(l,r)表示對區間[l,r]進行排序後的結果

很容易想到:F[1,n]等於滿足L[k] = k的k的個數

而這題可以交換最多一次,那麼肯定是當前F[1,n]段中的其中一段再拆成多段,並且交換第一段和最後一段後,結果最優

也就是說,只需要暴力列舉當前每一段,然後對於當前這一段[l,r]

  1. 先求出所有的臨界點p滿足sort(p+1,r)和sort(l,p)連線後整體升序
  2. 之後對於所有相鄰的臨界點p1, p2,很顯然可以將字首[l,p1]和字尾[p2+1,r]作為單獨兩段,並交換這兩段後整體升序
  3. 這樣這一段就可以被拆成2+F[p1+1,p2]段

這樣的話對於當前段[l,r],最多就能再拆成max(2+F[p1+1,p2])段(其中p1≠l,p2≠r,注意特判)

求出最大值和k比較一下就好了

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
int a[1000005], b[1000005], L[1000005], L2[1000005], L3[1000005];
int Check(int l, int r, int bas)
{
	int i, sum;
	sum = L3[l-1] = 0;
	for(i=l;i<=r;i++)
	{
		L3[i] = max(L3[i-1], b[i]);
		if(L3[i]-bas==i-l)
			sum++;
	}
	return sum;
}
int Jud(int l, int r)
{
	int i, n, last, ans;
	n = r-l+1;
	for(i=1;i<=n;i++)
		b[i] = a[l+i-1]-l+1;
	last = 1, ans = 1;
	for(i=1;i<=n;i++)
	{
		L2[i] = min(L2[i-1], b[i]);
		if(i==1)
			L2[i] = b[i];
		if(n-L2[i]+1==i)
		{
			if(last==1 && i==n)
				continue;
			if(last==1 || i==n)
				ans = max(ans, 2);
			else
				ans = max(ans, Check(last, i, n-i+1)+2);
			last = i+1;
		}
	}
	return ans;
}
int main(void)
{
	int n, k, i, ans, last, sum;
	scanf("%d%d", &n, &k);
	ans = sum = 0;
	for(i=1;i<=n;i++)
	{
		scanf("%d", &a[i]);
		L[i] = max(L[i-1], a[i]);
		if(L[i]==i)
			sum++;
	}
	last = 1;
	for(i=1;i<=n;i++)
	{
		if(L[i]==i)
		{
			ans = max(ans, Jud(last, i)+sum-1);
			last = i+1;
		}
	}
	if(ans>=k)
		printf("Yes\n");
	else
		printf("Poor Simon\n");
	return 0;
}
/*
10 6
10 9 8 7 4 5 6 3 2 1
5 2
4 1 5 2 3
6 3
1 5 6 2 3 4
*/