出题人的手环
题目链接:https://ac.nowcoder.com/acm/contest/358/D
链接:https://ac.nowcoder.com/acm/contest/358/D
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
出题人的妹子送了出题人一个手环,这个手环上有 n 个珠子,每个珠子上有一个数。
有一天,出题人和妹子分手了,想把这个手环从两个珠子间切开,并按顺时针顺序展开成一条链。
可以发现,这条链一共有 n 种可能性。求这 n 种可能性的逆序对数之积模 1000000007。输入描述:
第一行一个数 n,表示珠子个数。
接下来一行 n 个数,以顺时针顺序给出每个珠子上的整数
输出描述:
一个数,表示答案。
备注:
n<=200000,-10^9<=珠子上的整数<=10^9。
个人思路:做到这题的时候,推出了规律,就是下一次可以在前一次的基础上减去小于第一个数的数,加上大于第一个数的数 但问题是怎么求得第一个环有多少个逆序对数,想了许久,发现怎么处理都要n*n 的复杂度,但是明显会
超时,所以这题写不下去了,之后看题解,都是用树状数组加上离散化的思想来做的,想想也是,只有用树log(n)的复杂度才能满足,但是这题怎么用树状数组呢?
都知道树状数组是树形结构,可以求区间和 和 单点更新,但是我们这题和区间和有什么关系呢? 我们要求的是逆序对数,也就是一个区间内在一个数后面的数比它小 因为顺序是递增的 逆序就是递减的咯。
我们怎么求区间内 后面的比它小的数的个数呢? 首先,把每个数在序列中从小到大排第几算出来,相等的话就并列排第几,排第几也就是该数在序列中第几小,然后这个顺序就可以当做树状数组的下标了,
但是还有一个问题,我们要求的是在这个数后面比它小的数,而不是整个序列中比它小的数,所以按照原始顺序插入的同时计算后面有多少个比它小的,其实就是减去前面有多少个比它大的,这就是答案了。
具体看代码:
#include<iostream> #include<algorithm> using namespace std; const int maxn=200000+5; const int mod=1000000007; typedef long long ll; ll n; int a[maxn];//a[i]表示 原位置为i的数 在序列中从小到大排第几 int c[maxn];//树状数组存储结构 struct Node{ int v,w;//v代表值 w代表位置 }node[maxn]; bool cmp(const Node a,const Node b) { return a.v<b.v; } int lowbit(int x) { return x&(-x); } void updata(int pos,int v) { while(pos<=n) { c[pos]+=v; pos+=lowbit(pos); } } int getsum(int pos)//求有多少个数比它小 { int sum=0; while(pos>0) { sum+=c[pos]; pos-=lowbit(pos); } return sum; } int main() { cin>>n; for(int i=1;i<=n;i++)//存储对应的值和所在的位置 { cin>>node[i].v; node[i].w=i; } sort(node+1,node+1+n,cmp);//按大小从小到大排序 a[node[1].w]=1;//最小的数显然是排第一 int pos=1; for(int i=2;i<=n;i++)//存取每个数按从小到大排序 能排第几 { if(node[i].v==node[i-1].v) a[node[i].w]=pos;//相等的话则为首个数的位置 else a[node[i].w]=++pos; } ll ans=0; ll re=1; for(int i=1;i<=n;i++) { updata(a[i],1);//插入树状数组 ans=(ans+(i-getsum(a[i])+mod))%mod;//i是原位置 a[i]是原位置的数所排大小 getsum(a[i]) 该大小的数前面有几个数 } re=(re*ans)%mod; ll ad; ll sub; for(int i=1;i<n;i++)//求出了第一种情况的,接下来就是 每一次在前一次的基础上 减掉小于第一个数的数 加上大于第一个数的数 { ad=(n-getsum(a[i])); sub=getsum(a[i])-(getsum(a[i])-getsum(a[i]-1));//注意这里是 a[i]-1 而不是 a[i-1] !! 我就错在这了 ans=(ans-sub+mod+ad)%mod; re=(re*ans)%mod; } cout<<re<<endl; }
当初的梦想实现了吗,事到如今只好放弃吗~