2017 多校训练第一场 HDU 6044 Limited Permutation

题意:有n个区间,对于第i个区间[li,ri]有li<=i<=ri,
对于任意1<=L<=i<=R<=n,当前仅当li<=L<=i<=R<=ri时P[i]=min(P[L],P[L+1],...,P[R])


题解:
首先要理解题意:当前仅当li<=L<=i<=R<=ri时P[i]=min(P[L],P[L+1],...,P[R])
因此对于P[i]一定有P[i]>P[li-1]且P[i]>P[ri+1],进一步说区间[li,ri](除了[1,n])一定被某个区间[lj,rj]包含,且j=li-1或j=ri+1
即区间j可分成[lj,j-1]和[j+1,rj]


我们把n个区间按L升序R降序进行排序(这样得到的区间LR正是前序遍历的区间)。得到的第1个区间一定要是[1,n](1比任何数都小),否则不合法,输出0;设这个区间对应的是第i个数,因此区间可再分为[1,i-1]和[i+1,n],看是否有这2个区间,如果没有则不合法,输出0...直到区间不可再分。


现在再来考虑方法数:设f(i)为区间i内的方法数,u,v分别为左右子区间,i内一共有ri-li+1个数,除去中间一个,要从中选i-li个数放入左区间,剩下的放入右区间,因此答案为:f(u)*f(v)*C(ri-li,i-li)
#include <bits/stdc++.h>
// #define test TEST 
using namespace std;
const int mod=1e9+7;
const int maxn=1e6+6;

typedef long long ll;
namespace IO {  
    const int MX = 4e7; //1e7占用内存11000kb  
    char buf[MX]; int c, sz;  
    void begin() {  
        c = 0;  
        sz = fread(buf, 1, MX, stdin);  
    }  
    inline bool read(int &t) {  
        while(c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;  
        if(c >= sz) return false;  
        bool flag = 0; if(buf[c] == '-') flag = 1, c++;  
        for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';  
        if(flag) t = -t;  
        return true;  
    }  
} 

typedef struct Node{
	int l,r,id;
	bool operator < (const Node &a) const{
		if(l!=a.l) return l<a.l;
		else return r>a.r;
	}
}node;
node seg[maxn];
ll fac[maxn],inv[maxn];

ll Power_Mod(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1ll){
			ans=ans*a%mod;
		}
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

void init(){
	fac[0]=1;
	for(int i=1;i<maxn;i++){
		fac[i]=fac[i-1]*i%mod;
	}
	inv[1]=1;
	for(int i=2;i<maxn;i++){
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	inv[0]=1;
	for(int i=1;i<maxn;i++){
		inv[i]=inv[i-1]*inv[i]%mod;
	}
}

ll C(ll a,ll b){
	return fac[a]*inv[b]%mod*inv[a-b]%mod;
}

// void init() {  
//     fac[0] = 1;  
//     for (int i = 1; i < maxn; i++) fac[i] = fac[i - 1] * i % mod;  
//     inv[maxn - 1] = Power_Mod(fac[maxn - 1], mod - 2);  
//     for (int i = maxn - 2; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;  
// }  
// ll C(ll n, ll m) {  
//     ll ret = 1;  
//     while (n && m) {  
//         ll nn = n % mod, mm = m % mod;  
//         if (nn < mm) return 0;  
//         ret = ((ret * fac[nn] % mod) * inv[mm] % mod) * inv[nn - mm] % mod;  
//         n /= mod, m /= mod;  
//     }  
//     return ret;  
// } 

int flag,rear;

ll dfs(int l,int r){
	if(!flag) return 0;
	if(l>r) return 1;
	if(seg[rear].l!=l || seg[rear].r!=r){
		flag=0;
		return 0;
	}
	node now=seg[rear++];
	ll ret=C(now.r-now.l,now.id-now.l)*dfs(now.l,now.id-1)%mod;
	ret=(ret*dfs(now.id+1,now.r))%mod;
	return ret;
}

int main(int argc, char const *argv[])
{
	#ifdef test
	freopen("test.txt","r",stdin);
	#endif 
	init();
	IO::begin();
	int cas=1,n;
	while(IO::read(n)){
		for(int i=1;i<=n;i++) {
			seg[i].id=i;
			IO::read(seg[i].l);
		}
		for(int i=1;i<=n;i++) 
			IO::read(seg[i].r);
		sort(seg+1,seg+n+1);
		flag=rear=1;
		printf("Case #%d: %lld\n",cas++,dfs(1,n));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值