jzoj6302. 提高组

8 篇文章 0 订阅
  • 感觉好像没6301难,但是思路特别巧妙。
  • 对于一个合法的序列,我们都可以用两个(没有交集)上升子序列将其覆盖,如果已经是有序的话也不影响
  • 考虑这个序列的前缀max,我们将那些对前缀max有贡献的位置(就是一个连续段开头)拉出来,一定是一个上升子序列,另一方面,设两个数ai,ai+1,ai对前缀max有贡献,且ai+1对max无贡献(ai+1<ai),那么ai+1后面的数都必须要比ai+1大
  • 还有,如果我们确定了前缀max,那么整个序列必定是唯一确定的(因为剩下的只能从小到大一次填入)
  • 那我们只要求有多少个max序列
  • 考虑它的性质,maxi<=maxi+1,maxn=n,i<=maxi
  • 放到平面上就是求(1,1)到(x,y)再由(x,y)走到(n,n)且不能过y=x-1这条线,翻折法即可。
  • (a,b)这个点表示在a这个位置,当前前缀max是b,因为我们第一个数不一定是1,但是我们可以先走到(1,2),(1,3)这些点,然后再走过去,相当于分别以1,2,3开头,又因为1,1到这些点的方案只有一个,故这样做是对的。
  • code
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=20000000+5;
typedef long long ll;
const ll mo=1000000007;
ll inv[N],fac[N],ans1,ans2;
int n,m,x,y;
void R(int &x){
	int t=0; char ch;
	for (ch=getchar();!('0'<=ch&&ch<='9');ch=getchar());
	for (;('0'<=ch&&ch<='9');ch=getchar())t=t*10+ch-'0';
	x=t;
}
ll C(ll x,ll y){
	if (x<y) return 0;
	return fac[x]*inv[x-y]%mo*inv[y]%mo;
}
ll power(ll a,ll b){
	ll t=1ll,y=a%mo;
	while (b){
		if (b&1) t=t*y%mo;
		y=y*y%mo;
		b/=2;
	}
	return t;
}
ll F(ll x){
	return (x%mo+mo)%mo;
}
int main(){
	freopen("tg.in","r",stdin);
	freopen("tg.out","w",stdout);
	int T;
	R(T);
	fac[0]=1;inv[0]=1;
	fo(i,1,N-1) fac[i]=fac[i-1]*(ll)i%mo;
	inv[N-1]=power(fac[N-1],mo-2);
	fd(i,N-2,1) inv[i]=inv[i+1]*(ll)(i+1)%mo;
	while (T--){
		R(n);R(x);R(y);
		if (x>y) swap(x,y);
		x--; y--;  n--;
		ans1=F(C(x+y,x)-C(y+x,y+1));
		ans2=F(C(2*n-x-y,n-x)-C(2*n-x-y,n-y-1));
		printf("%lld\n",ans1*ans2%mo);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值