AGC065 D. Not Intersect
|字数总计:1.1k|阅读时长:5分钟|阅读量:13
题目链接:https://atcoder.jp/contests/agc065/tasks/agc065_d
题目大意:圆上有 个点,问有多少种连法,使得有 条边且边不交叉(可以在端点处相交)
做法
神仙组合意义,投降。
当 或者 时可以直接输出答案,下面默认
设 表示 个点 条边的答案, 表示 个点 条边且允许重边的答案。
显然有:
则由二项式反演有: 。
现在考虑怎么计算 ,考虑边不能交叉这个条件,如果你足够智慧,你就会发现有一个结构很符合这个要求:栈。
现在我们用这么一个过程去描述连边的过程:
从 到 ,到达 的时候,先弹出栈内的若干元素,再往栈内加入若干个 ,到最后栈为空,如果 在 的时候弹出就代表了一条 的边。
显然,这个过程一一对应,现在就是计数了。
可以发现, 只能入栈, 只能出栈,所以我们不妨改为 次入栈和出栈。(接下来假设要求 的值)
现在就是 表示入栈的次数, 表示出栈的次数,满足:
显然,一个合法的 序列代表了一条从 到 只能向右或者向上的的路径, 同理。
则一个合法的 序列对应了两条合法的路径,且满足一条路径始终在另外一条路径下端。
可以发现,我们设 为向右, 为向上,假设我们能计算有 个 , 个 时有多少对路径满足第二条路径在第一条路径下方,称作函数 ,那么我们所求的就是 。
研究第 步,可以发现就四种情况: ,可以发现,就是要让每个时刻 的数量不少于 的数量,如果用折线法描述就是上面是: ,然后不要让折线低过 轴,显然, 的数量是一样的,枚举 的数量就可以知道四种情况的数量了:
然后直接算就行了。
时间复杂度: 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include<bits/stdc++.h> using namespace std; typedef long long LL; const LL mod=1e9+7; const int N=3e7+105; LL fc[N],nfc[N]; void init(){ const int L=3e7+3; fc[0]=fc[1]=nfc[0]=nfc[1]=1;for(int i=2;i<=L;i++)nfc[i]=(mod-mod/i)*nfc[mod%i]%mod; for(int i=2;i<=L;i++)nfc[i]=nfc[i-1]*nfc[i]%mod,fc[i]=fc[i-1]*i%mod; } int n;LL m; LL g[N]; LL calc(int a,int b){ return fc[a+b+1]*fc[a+b]%mod*nfc[a]%mod*nfc[b]%mod*nfc[a+1]%mod*nfc[b+1]%mod; } LL C(int x,int y){ return fc[x]*nfc[y]%mod*nfc[x-y]%mod; } int main(){ init(); scanf("%d%lld",&n,&m); if(!m){ printf("1\n"); return 0; } if(m>n+n-3){ printf("0\n"); return 0; } for(int i=1;i<=m;i++)g[i]=calc(n-2,i); LL ans=0; for(int i=1;i<=m;i++){ if((m-i)&1)ans=(ans-g[i]*C(m-1,i-1)%mod+mod)%mod; else ans=(ans+g[i]*C(m-1,i-1))%mod; } printf("%lld\n",ans); return 0; }
|
坑
没有生成函数做法。