小明这些天一直在思考这样一个奇怪而有趣的问题:
在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:
如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。
当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。
输出一个整数,表示不同连号区间的数目。
3 2 4 1
3 4 2 5 1
9
这题咋说呢。。。感觉自己是运气通过,我直接用暴力,虽然明知道会超时,还是试了一下
说下我的思路
1.因为一个数是肯定成立, 就不讨论了,在最后的结果加上n就行
2.从第一个数开始,初始长度为2,一直到最后一位,如果这些数据是满足的,那肯定用对最大值,最小值还有长度进行求和((min+max)*len/2;)这个数肯定等于这次区间的总和。还有最大值-最小值肯定是等于长度的(因为数字都是连续的)
这样做就避免了排序带来的复杂度。
我一提交80分。感觉还行,暴力解决这么大的数还可以,我报着试一试的心理加了一个判断条件
if(abs(a[k]-a[i])>j-i)
如果区间里面的数减去第一个>长度的,肯定不符合
这样提交也是80..
再加点呗
if(abs(a[k]-a[i])>j-i||(abs(a[k]-a[k+1])>j-i&&k!=j))
前一个减去后一个也不能>长度,反正就是试试的心理
过了...
题目关键字是并查集,不会用。写上再说
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int m[50001];
int co=0;
int find(int i){
while(i!=m[i])
i=m[i];
return i;
}
void add(int i,int j){
int x=find(i);
int y=find(j);
if(x!=y){
m[x]=y;
}
}
int SUM(int min,int max,int len){
return (min+max)*len/2;
}
int main(){
int n;
int a[50001];
cin>>n;
for(int i=0;i<n;i++) {
cin>>a[i];
}
for(int i=0;i<n-1;i++){
for(int j=i+1;j<n;j++){
int sum=0,min=1000000,max=0;
for(int k=i;k<=j;k++){
if(abs(a[k]-a[i])>j-i||(abs(a[k]-a[k+1])>j-i&&k!=j))
break;
if(a[k]>max)
max=a[k];
if(a[k]<min)
min=a[k];
sum+=a[k];
}
if(j-i+1==max-min+1){
int su=SUM(min,max,j-i+1);
if(su==sum)
co++;
}
}
}
cout<<co+n;
return 0;
}