Description
平面上有 N(N≤〖10〗^5 ) 个点,每个点的横、纵坐标的范围都是 1~N,任意两个点的横、纵坐标都不相同。
若三个点 (x_1,y_1),(x_2,y_2),(x_3,y_3 ) 满足 x_1<x_2<x_3, y_1>y_2 并且 y_3>y_2,则称这三个点构成"v"字图腾。
若三个点 (x_1,y_1),(x_2,y_2),(x_3,y_3 ) 满足 x_1<x_2<x_3, y_1<y_2 并且 y_3<y_2,则称这三个点构成"^"字图腾。
求平面上"v"和"^"字图腾的个数。
若三个点 (x_1,y_1),(x_2,y_2),(x_3,y_3 ) 满足 x_1<x_2<x_3, y_1>y_2 并且 y_3>y_2,则称这三个点构成"v"字图腾。
若三个点 (x_1,y_1),(x_2,y_2),(x_3,y_3 ) 满足 x_1<x_2<x_3, y_1<y_2 并且 y_3<y_2,则称这三个点构成"^"字图腾。
求平面上"v"和"^"字图腾的个数。
Input
第一行一个数n
第二行是n个数,分别代表y1,y2……yn
第二行是n个数,分别代表y1,y2……yn
Output
两个数中间用空格隔开 依次为V的个数和∧的个数
Sample Input
5
1 5 3 2 4
Sample Output
3 4
这题乍一看水题,暴力O(n^2)好像可以过
仔细算就会发现,不行!!!
那我们就需要一种O(nlogn),甚至O(n)的算法
看“v”和“^”的满足条件有一些是不是很像线段树求逆序对呢
逆序对求法(线段树):逆序对
(其实我也不知道为什么,队测时自然想到线段树,没敢敲(吐血),错估了时间复杂度,以为是不如暴力的O(n^2logn))
还有一点,至少乘法原理你得想到吧,不知道的自己去学
先给暴力做法:
1 #include<cstdio> 2 int n,m,d[100001];long long ans1,ans2; 3 int main() 4 { 5 scanf("%d",&n); 6 for(int i=1;i<=n;i++)scanf("%d",&d[i]); 7 for(int i=1;i<=n;i++) 8 { 9 int lx=0,rx=0,ll=0,rr=0; 10 for(int j=1;j<i;j++) 11 if(d[j]<d[i])lx++; 12 else ll++; 13 for(int j=i+1;j<=n;j++) 14 if(d[j]<d[i])rx++; 15 else rr++; 16 ans1+=ll*rr; 17 ans2+=lx*rx; 18 } 19 printf("%lld %lld",ans1,ans2); 20 }
这个是最朴实无华的暴力了(TLE大法)
请看这句话(每个点的横、纵坐标的范围都是 1~N)
依据这个,可以小小优化暴力,下面给出暴力优化(并没有什么用,依然是TLE大法):
1 #include<cstdio> 2 int n,m,d[100001];long long ans1,ans2; 3 int main() 4 { 5 scanf("%d",&n); 6 for(int i=1;i<=n;i++)scanf("%d",&d[i]); 7 for(int i=1;i<=n;i++) 8 { 9 int lx=0,ll=0; 10 for(int j=1;j<i;j++) 11 if(d[j]<d[i])ll++; 12 else lx++; 13 ans1+=ll*(d[i]-1-ll); 14 ans2+=lx*(n-d[i]-lx); 15 } 16 printf("%lld %lld",ans2,ans1); 17 }
以上纯属娱乐,相信大家都会写
那个优化可有用,那么让我们来看正解吧
线段树(或树状数组)
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int n,ans1,ans2;long long ans0,ans; 5 struct oo{int a,b,v;}s[800001]; 6 void build(int x,int l,int r) 7 { 8 s[x].a=l,s[x].b=r; 9 if(l==r)return ; 10 build(x<<1,l,l+r>>1); 11 build(x<<1|1,(l+r>>1)+1,r); 12 } 13 void change(int x,int y) 14 { 15 s[x].v++; 16 if(s[x].a==s[x].b)return ; 17 int mid=s[x].a+s[x].b>>1; 18 if(y<=mid)change(x<<1,y); 19 else change(x<<1|1,y); 20 } 21 void get(int x,int y) 22 { 23 if(y>s[x].b){ans1+=s[x].v;return ;} 24 if(y<s[x].a){ans2+=s[x].v;return ;} 25 if(s[x].a==s[x].b)return ; 26 get(x<<1,y);get(x<<1|1,y); 27 } 28 int main() 29 { 30 scanf("%d",&n); 31 build(1,1,200000); 32 for(int i=1,x;i<=n;i++) 33 { 34 ans1=ans2=0; 35 scanf("%d",&x),change(1,x); 36 get(1,x); 37 ans0+=ans1*(x-1-ans1),ans+=ans2*(n-x-ans2); 38 } 39 printf("%lld %lld",ans,ans0); 40 }
很短吧!!
祝大家早日AC!!!