• AGC053EMore Peaks More Fun【计数】


    正题

    题目链接:https://atcoder.jp/contests/agc053/tasks/agc053_e


    题目大意

    给出\(n\)个二元组\((a_i,b_i)\),求有多少个\(1\sim n\)的排列\(p\)满足:

    1. 你可以将一些\((a_i,b_i)\)变为\((b_i,a_i)\)
    2. 定义序列\(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\)那么答案就是

    \[\prod_{i=1}^{x-1}f_i\times \prod_{i=x+1}^{y-1}(f_{i}+1)\times \prod_{i=y+1}(f_{i}+2) \]

    用个树状数组计数就好了,时间复杂度:\(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;
    }
    
  • 相关阅读:
    对于进程的理解
    反汇编引擎实现——流程分析
    window异常处理——except_handler4以及栈展开分析
    对于硬盘驱动的理解
    对文件系统的理解
    移动端适配flexible.js
    vue学习(5)-评论功能(利用父组件的方法)
    vue学习(4)-组件的创建,父子组件传值,$refs
    vue学习(3)-增删改查
    vue学习(2)-过滤器
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16292724.html
Copyright © 2020-2023  润新知