牛客國慶集訓派對Day1: K. Tengen Toppa Gurren Lagann(貪心)
阿新 • • 發佈:2019-02-02
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]
- 先求出所有的臨界點p滿足sort(p+1,r)和sort(l,p)連線後整體升序
- 之後對於所有相鄰的臨界點p1, p2,很顯然可以將字首[l,p1]和字尾[p2+1,r]作為單獨兩段,並交換這兩段後整體升序
- 這樣這一段就可以被拆成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
*/