题解:P9255 [PA 2022] Podwyżki

· · 题解

题目链接

题意简述

要将一个区间长度为 n 的区间划分为 k 段,使得从每一段中任意选出一个数组成的长度为 k 的子序列都不是严格上升的序列。输出划分方案,或报告无解。

特别地,长度为 1 的序列是严格上升序列。

思路分析:

分类讨论。

$k=2$ 时,枚举两个区间中间的断点,如果左区间的最小值大于等于右边的最大值,那么就有解。预处理出前缀最大值和后缀最小值。 $k=3$ 时,考虑中间的区间选那个数,直接枚举这个数作为中间的区间,比较左右区间,做法与 $k=2$ 时类似。 当 $k \ge 4$ 时,有一个很显然的构造方案,就是如果有一个 $i$ 满足 $a_i>=a_{i+1}$,就可以将它们单独划分开,这样一定会选到这两个数,从而让所选出的序列不严格上升。这样这两个数一共占了两段,剩下左边一段,右边一段正好满足 $k=4$ 的情况。对于 $k>4$ 的情况,将 $[1,i-1]$ 和 $[i+2,n]$ 任意划分够 $k=2$ 段即可。 # 代码: ```cpp //code by xxxaIq #include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=500003; int read(){ int x=0,f=1; char ch=getchar(); while(ch>57||ch<48){ if(ch==45){ f=-1; } ch=getchar(); } while(ch>=48&&ch<=57){ x=(x<<1)+(x<<3)+(ch-48); ch=getchar(); } return x*f; } int n,k,a[maxn],p=0,ma[maxn],mi[maxn]; void out(int x){ int r=0,d; cout<<"TAK\n"; d=(x!=n); for(int i=1;i<x-2;i++){ if(k-d-4>=0){ k--; r=i; cout<<i<<" "; }else{ break; } } if(x>2){ cout<<x-2<<" "; r=x-2; k--; } cout<<x-1<<" "; if(x!=n){ cout<<x<<" "; } k-=2; r=x; for(int i=x+1;i<n;i++){ if(k-2>=0){ k--; r=i; cout<<i<<" "; }else{ break; } } return; } int main(){ n=read(),k=read(); for(int i=1;i<=n;i++){ a[i]=read(); } if(k==1){ cout<<"NIE\n"; return 0; } mi[1]=a[1]; for(int i=2;i<=n;i++){ mi[i]=min(mi[i-1],a[i]); } for(int i=n;i>=1;i--){ ma[i]=max(ma[i+1],a[i]); } if(k==2){ for(int i=1;i<n;i++){ if(mi[i]>=ma[i+1]){ cout<<"TAK\n"<<i<<"\n"; return 0; } } cout<<"NIE\n"; return 0; } if(k==3){ for(int i=2;i<n;i++){ if(mi[i-1]>=a[i]||a[i]>=ma[i+1]){ cout<<"TAK\n"; cout<<i-1<<" "<<i<<"\n"; return 0; } } cout<<"NIE\n"; return 0; } for(int i=2;i<=n;i++){ if(a[i-1]>=a[i]){ out(i); return 0; } } cout<<"NIE\n"; return 0; } ```