正题
题目链接:https://atcoder.jp/contests/agc053/tasks/agc053_e
题目大意
给出\(n\)个二元组\((a_i,b_i)\),求有多少个\(1\sim n\)的排列\(p\)满足:
- 你可以将一些\((a_i,b_i)\)变为\((b_i,a_i)\)
- 定义序列\(x_{2i-1}=a_{p_i},x_{2i}=b_{p_i}\),使得\(x\)恰好有\(n-1\)个峰值。
保证\(a_i,b_i\)互不相同。
对\(10^9+7\)取模
\(1\leq n\leq 2\times 10^5,1\leq a_i,b_i\leq 2n\)
解题思路
考虑怎样的排列满足条件,因为每个二元组的位置最多产生一个峰值,所以我们可以考虑没有产生峰值的位置。
先令所有的\(a_i<b_i\),那么如果是最后一个二元组没有产生峰值,那么我们应该会排列成\(a\dot{b}a\dot{b}a\dot{b}a\dot{b}a\dot{b}...ab\)的形式,那么就要求了\(b_i>a_{i+1}\)。
如果是中间的某个位置\(p\)没有产生峰值,那么应该是\(a\dot{b}a\dot{b}...ab\dot{b}a\dot{b}a\dot{b}a\dot{b}a...\dot{b}a\)的形式。
那么就要求\(b_i>a_{i+1}(i<p),b_i\geq a_{i+1}(i>p+1),b_{p+1}>b_{p}\),有可能我们将\(p+1\sim n\)都翻转一下也是合法的,所以为了防止计算重复,我们强制要求\(a_{p+1}>b_p\)就可以了。
那么考虑如何计数,我们记\(f_i\)表示满足\(b_j>b_i,a_j<a_i\)的二元组个数,然后我们从\(b\)小的到\(b\)大的填。
那么对于第一种情况,我们答案就是\(\prod_{i=1}^n (f_i+1)\)。至于第二种情况,我们枚举考虑\(p_i\)和\(p_{i+1}\)分别是\(x,y\)那么答案就是
用个树状数组计数就好了,时间复杂度:\(O(n\log n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;
const ll N=4e5+10,P=1e9+7;
struct node{
ll a,b;
}a[N];
ll n,w[N],t[N],f[N][3];
void Change(ll x,ll val){
while(x<=2*n){
(t[x]+=val)%=P;
x+=lowbit(x);
}
return;
}
ll Ask(ll x){
ll ans=0;
while(x){
(ans+=t[x])%=P;
x-=lowbit(x);
}
return ans;
}
ll power(ll x,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*x%P;
x=x*x%P;b>>=1;
}
return ans;
}
ll inv(ll x)
{return power(x,P-2);}
bool cmp(node x,node y)
{return x.b<y.b;}
signed main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld%lld",&a[i].a,&a[i].b);
if(a[i].a>a[i].b)swap(a[i].a,a[i].b);
}
sort(a+1,a+1+n,cmp);
ll ans=1;
for(ll i=n;i>=1;i--){
w[i]=Ask(a[i].b);
ans=ans*(w[i]+1)%P;
Change(a[i].a,1);
}
f[0][0]=f[0][1]=f[0][2]=1;
for(ll i=1;i<=n;i++)
for(ll j=0;j<3;j++)
f[i][j]=f[i-1][j]*(w[i]+j)%P;
memset(t,0,sizeof(t));
for(ll i=1;i<=n;i++){
(ans+=Ask(a[i].a)*f[n][2]%P*inv(f[i][2])%P*f[i-1][1]%P)%=P;
Change(a[i].b,f[i-1][0]*inv(f[i][1])%P);
}
printf("%lld\n",ans);
return 0;
}