k种不同的数弄一个排列,第i种数的个数有ci个。要求最后一个i+1位于最后一个i的后面。求合法的排列数。
yy了一个公式,居然过了样例。统计c1~ci的和tot,初始方案数为1,对于每一个数i,方案数要乘上C(tot-1,ci-1),算到最后就是答案。由于要取模,组合数的计算使用了预处理阶乘+逆元。
为什么可以这样做呢。可以倒过来想。因为i=k必须是最后一个数,然后剩下的ck-1可以随意放,共有C(tot-1,ck-1)种方案,i=k-2时同理,就得到上述公式。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll mod=1e9+7;
ll c[1010];
//扩展欧几里德
void ExEuclid(ll a,ll b,ll &x,ll &y,ll &q){
if(b==0){
x=1;y=0;q=a;
return;
}
ExEuclid(b,a%b,y,x,q);
y-=x*(a/b);
}
//乘法逆元
ll inv(ll num){
ll x,y,q;
ExEuclid(num,mod,x,y,q);
if(q==1)return (x+mod)%mod;
}
ll fab[1000010];
//组合数
ll C(ll n,ll k){
ll res=fab[n]*inv(fab[k]);
res%=mod;
res*=inv(fab[n-k]);
res%=mod;
return res;
}
int main(){
int k;
cin>>k;
fab[0]=1;
for(int i=1;i<=1000000;i++){
fab[i]=fab[i-1]*i;
fab[i]%=mod;
}
ll tot=0;
ll ans=1;
for(int i=1;i<=k;i++){
cin>>c[i];
tot+=c[i];
if(i>1&&c[i]>1){
ans*=C(tot-1,c[i]-1);
ans%=mod;
}
}
cout<<ans<<endl;
return 0;
}
1~n的排列,写成循环的写法。然后循环写法可能有多种,为它重新排序,使得循环按从左到右的顺序排列;在循环内部,把最大的元素放在首位,称为“标准写法”。有些排列的“标准写法”和排列本身一样,求第k个(第k小的)这样的排列。
也是组合数学的脑洞题。。分析一下可以发现,所谓标准写法,就是只能交换相邻两个数,交换过的数不能再次交换。比如说,最小的合法排列就是1~n的升序,次小的是1~n升序交换最后两个数。
为了生成答案,我们可以通过若干次交换操作来得到。再分析一下,肯定是优先交换尽可能靠右边的两个数,然后可以惊奇地发现,交换某个位置的数,对大小次序的影响居然和斐波那契数有关。具体比较难描述,见代码。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int ans[55];
ll Fibonacci[100];
int main(){
Fibonacci[0]=1;
Fibonacci[1]=1;
for(int i=2;i<=91;i++){
Fibonacci[i]=Fibonacci[i-1]+Fibonacci[i-2];
}
ll n,k;
while(cin>>n>>k){
k--;
for(int i=0;i<=n;i++){
ans[i]=i;
}
while(k){
int t;
for(t=1;t<=91;t++){
if(Fibonacci[t]>k)break;
}
swap(ans[n-t+1],ans[n-t+2]);
k-=Fibonacci[t-1];
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
}
return 0;
}
有n个人,彼此之间互相love或者hate。对于任意的三个人之间,你不能容忍这两种情况:一是三个人互相hate,二是其中两人hate但是love另外一个人。已知m组关系,问有多少种合法的关系。
看了一下题解,上面讲了一个结论。如果n个人的关系合法,那么他们肯定组成一个二分图。同一部的人互相love,不同部的人hate。然后我们就可以用带关系的并查集去解决问题。m种已知的关系可能会形成若干的小团体(也可以是独立的个人),固定第一个团体,余下的每个团体都有两种选择。当然已知关系如果已经矛盾了,那答案就是0。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int fa[100010];
bool flag[100010];
bool root[100010];
int find(int x){
if(fa[x]==x)return x;
int tmp=fa[x];
fa[x]=find(fa[x]);
if(flag[x]==flag[tmp])flag[x]=1;
else flag[x]=0;
return fa[x];
}
void Union(int a,int b,int c){
fa[a]=b;
flag[a]=c;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
fa[i]=i;
flag[i]=1;
}
bool ok=1;
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int fa=find(a);
int fb=find(b);
if(fa!=fb){
if(!flag[a])c=!c;
if(!flag[b])c=!c;
Union(fa,fb,c);
}else{
if(c){
if(flag[a]!=flag[b])ok=0;
}else{
if(flag[a]==flag[b])ok=0;
}
}
}
if(!ok){
cout<<0<<endl;
return 0;
}
for(int i=1;i<=n;i++){
find(i);
root[fa[i]]=1;
}
int cnt=0;
for(int i=1;i<=n;i++){
cnt+=root[i];
}
int ans=1;
cnt--;
while(cnt--){
ans<<=1;
ans%=1000000007;
}
cout<<ans<<endl;
return 0;
}