(sf D-Rating Compression)
题目
简要题意
给定一个长度为 (n) 的序列 (a),定义 “(k) 级青白给” 为一个序列 (b),其中每个元素 (b_i) 可以被表示为 (min_{j=i}^{i+k-1} a_j)。
现在请你判断,对于 (i=1ldots n),“(i) 级青白给” 是否为一个排列。
数据范围与提示
有多组数据 (le 10^4),(1le nle 3 imes 10^5, 1le a_ile n),且 (sum nle 3 imes 10^5)。
解法
#1. 一种方法
“(1) 级青白给” 比较特殊,特判序列 (a) 本身是否为排列即可。
观察题目样例可得,“(i) 级青白给” 是否为排列是有依赖性的。即若 “(i+1) 级青白给” 不是排列,那么 “(i) 级青白给” 也不是排列。
为什么呢?考虑不合法的 “(i+1) 级青白给”,要么 (a) 没有 (1ldots n-i) 种数,要么 (b) 出现了重复的数字。
如果没有这么多种,肯定也无法满足 “(i) 级青白给” 的标准;如果出现重复数字,级别越小重复概率越大。
这样我们自然地想到从 “(n) 级青白给” 开始判断,直接判断区间最小值是否为 (1)。对于 “(n-1) 级青白给”,我们需要保证 (1) 在 (1) 或 (n) 号位,且序列存在 (2),以此类推…
我们发现,这类似于将区间 ([1,n]) 从边界缩小的过程。
注意对于重复的数字 (x),它会使级别小于 (n-x+1) 的青白给不为排列,但除此之外的青白给是不受影响的。
#2. 另一种方法
(mathtt {By} sf xjx)。
其实是另一种实现方法。
定义 void solve(l,r,m,d)
为当前区间为 ([l,r]),还有 “([1,m]) 级别的青白给” 没有填答案,填到数字 (d)(当然也可以用循环)。
详见注释。
代码
#1. 一种方法
#include <bits/stdc++.h>
using namespace std;
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
const int maxn=3e5+5;
vector <int> ans;
int n,a[maxn],num[maxn];
int main() {
rep(T,1,read(9)) {
n=read(9); ans.clear();
rep(i,1,n) a[i]=read(9),++num[a[i]];
int l=1,r=n;
rep(i,1,n-1) {
if(num[i]) ans.push_back(1);
if(a[l]==i or a[r]==i) {
if(a[l]==i) ++l;
else --r;
}
else break;
if(num[i]>1) break;
}
rep(i,1,n-ans.size()-1) ans.push_back(0);
bool flag=true;
rep(i,1,n) {
if(!num[a[i]]) flag=false;
num[a[i]]=0;
}
ans.push_back(flag);
for(int i=ans.size()-1;i>=0;--i) write(ans[i]); puts("");
}
return 0;
}
#2. 另一种方法
#include<cstdio>//JZM YYDS!!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<ctime>
#define ll long long
#define MAXN 300005
#define uns unsigned
#define MOD 998244353ll
#define INF 0x3f3f3f3f
#define lowbit(x) ((x)&(-(x)))
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
int n,a[MAXN],num[MAXN],b[MAXN];
bool ans[MAXN];
inline void solve(int l,int r,int m,int d){
if(l>r||d>n)return;
// 未出现此数字
if(num[d]==0){
for(int i=1;i<=m;i++)ans[i]=0;
return;
}
// 此数字出现不止一次
if(num[d]>1){
for(int i=1;i<=min(r-l,m);i++)ans[i]=0;
// r-l+1 已经确定
return;
}
// 此数字只出现一次却不在边界上
if(b[d]>l&&b[d]<r){
for(int i=2;i<=min(r-l,m);i++)ans[i]=0;
solve(l,r,1,d+1);
// 特判 k=1 的情况
}
else if(b[d]==l)solve(l+1,r,min(m,r-l),d+1);
else solve(l,r-1,min(m,r-l),d+1);
}
signed main()
{
for(int T=read();T--;){
n=read();
for(int i=1;i<=n;i++)num[i]=0;
for(int i=1;i<=n;i++)
a[i]=read(),b[a[i]]=i,num[a[i]]++,ans[i]=1;
solve(1,n,n,1);
for(int i=1;i<=n;i++)putchar('0'+ans[i]);
putchar('
');
}
return 0;
}
(sf F-The Struggling Contestant)
解法
可以看一看这个太太的博客,我只是基于自己不太懂的地方进行阐释。
- 为什么 (f(z)le 2k+2-f(x))。总端点数减去 (x) 作为端点出现的次数。
- 最多只会存在一个 (x) 满足 (f ( x ) > k + 2)。同理,还是考虑总端点数为 (2k+2)。
最后需要特判作为端点次数最多的 (x) 是否能找到足够的 (y) 与其配对。
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
const int maxn=1e5+5;
int n,a[maxn],num[maxn],cnt[maxn];
int main() {
rep(T,1,read(9)) {
n=read(9); int k=0;
rep(i,1,n) a[i]=read(9),num[i]=cnt[i]=0;
rep(i,1,n) ++num[a[i]];
rep(i,2,n)
if(a[i]==a[i-1]) cnt[a[i]]+=2,++k;
++cnt[a[1]],++cnt[a[n]];
int max_cnt=0,max_num=0;
rep(i,1,n) {
max_cnt=Max(max_cnt,cnt[i]);
max_num=Max(max_num,num[i]);
}
if(max_num*2>n+1) puts("-1");
else print(k+Max(0,max_cnt-(k+2)),'
');
}
return 0;
}