Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov! 比赛人数10889
[codeforces 1330B] Dreamoon Likes Permutations 合理分块
总目录详见https://blog.csdn.net/mrcrack/article/details/103564004
在线测评地址https://codeforces.com/contest/1330/problem/B
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
B - Dreamoon Likes Permutations | GNU C++11 | Accepted | 77 ms | 2200 KB |
比赛时,没有思路,最好的办法就是,拿出纸笔,模拟样例数据生成过程
5
1 4 3 2 1
2
1 4
4 1
模拟发现,对应排列数
1
4 3 2 1
1出现2次
1 4 3 2
1
1出现2次
一组排列数含1个元素,另一组排列数含4个元素
可这样分组,按自左往右序列,分成1个元素(最左边有1个元素);5-1=4个元素(最左边有4个元素).这两种情况。如下:
(1) (4 3 2 1)此种情况正确
(1 4 3 2) (1)此种情况正确
6
2 4 1 3 2 1
1
4 2
模拟发现,对应排列数
2 4 1 3
2 1
1出现2次,2出现2次
一组排列数含2个元素,另一组排列数含4个元素
可这样分组,按自左往右序列,分成2个元素(最左边有2个元素);6-2=4个元素(最左边有4个元素).这两种情况。如下:
(2 4) (1 3 2 1)此种情况错误
(2 4 1 3) (2 1)此种情况正确
4
2 1 1 3
0
模拟发现,找不到,对应排列数
1出现2次
一组排列数含2个元素,另一组排列数含2个元素
可这样分组,按自左往右序列,分成2个元素(最左边有2个元素);4-2=2个元素(最左边有2个元素).这两种情况。如下:
(2 1) (1 3)此种情况错误
(2 1) (1 3)此种情况错误
4
1 3 3 1
0
模拟发现,找不到,对应排列数
1出现2次,3出现2次
一组排列数含1个元素,另一组排列数含3个元素
可这样分组,按自左往右序列,分成1个元素(最左边有1个元素);4-1=3个元素(最左边有3个元素).这两种情况。如下:
(1) (3 3 1)此种情况错误
(1 3 3) (1)此种情况错误
12
2 1 3 4 5 6 7 8 9 1 10 2
1
2 10
模拟发现,对应排列数
2 1
3 4 5 6 7 8 9 1 10 2
1出现2次,2出现2次
一组排列数含2个元素,另一组排列数含10个元素
可这样分组,按自左往右序列,分成2个元素(最左边有2个元素);12-2=10个元素(最左边有10个元素).这两种情况。如下:
(2 1) (3 4 5 6 7 8 9 1 10 2)此种情况正确
(2 1 3 4 5 6 7 8 9 1) (10 2)此种情况错误
3
1 1 1
0
模拟发现,找不到,对应排列数
1出现3次
不符题意,无需再做后面的操作。
比赛时,代码写得一团糟,第一次提交WA,反复读题,觉得思路没有问题,每组输入数据,对应的输出数据的第一行输出情况,只有0,1,2,三种情况。
举些数据,突然想到神来一笔,找到关键数据如下
Input:
1
4
1 2 2 1
Output:
1
2 2
若上述数据能通过,该题的坑点也就跳过去了。
修改代码,提交AC.
比赛时,虽然提交的代码效率高,但感觉可读性差了许多,以下AC代码,为赛后重写代码,可读性高了许多。
#include <cstdio>
#include <algorithm>
#define maxn 200010
using namespace std;
int a[maxn],cnt[maxn],n,b[maxn];//b[]临时数组
int judge(int l,int r){//返回值0表明a[]在[l,r]区间的元素,不能构成排列数.返回值1表明a[]在[l,r]区间的元素,能构成排列数
int i,m;
m=r-l+1;
for(i=1;i<=m;i++)b[i]=a[l+i-1];
sort(b+1,b+1+m);//自小到大排序
for(i=1;i<=m;i++)
if(b[i]!=i)return 0;//判定b[]在[l,r]区间,是否是排列数
return 1;
}
int main(){
int t,i,b,k,l1,r1,l2,r2;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(i=1;i<=n;i++)cnt[i]=0;
for(i=1;i<=n;i++)scanf("%d",&a[i]),cnt[a[i]]++;
b=0;
for(i=1;i<=n;i++)
if(cnt[i]==2)b=i;//记录最短排列数中元素的最大值
else break;
if(b==0)printf("0\n");
else{
k=0,l1=0,r1=0,l2=0,r2=0;
if(judge(1,b)&&judge(b+1,n))k++,l1=b,r1=n-b;
if(judge(1,n-b)&&judge(n-b+1,n))k++,l2=n-b,r2=b;
if(k==2){
if(b==n-b){//可能遇到排列数元素个数相同的情况。
printf("1\n");
printf("%d %d\n",b,n-b);
}else{
printf("2\n");
printf("%d %d\n",b,n-b);
printf("%d %d\n",n-b,b);
}
}else if(k==1){//需判定该输出b n-b,还是n-b b
printf("1\n");
if(l1)printf("%d %d\n",l1,r1);
else printf("%d %d\n",l2,r2);
}else printf("0\n");//找不到排列数
}
}
return 0;
}