Codeforces Round #309 (Div. 1) A B C

来源:互联网 发布:网络打印app 编辑:程序博客网 时间:2024/05/01 01:01

A Kyoya and Colored Balls

        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 longll 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;}

B Kyoya and Permutation

        1~n的排列,写成循环的写法。然后循环写法可能有多种,为它重新排序,使得循环按从左到右的顺序排列;在循环内部,把最大的元素放在首位,称为“标准写法”。有些排列的“标准写法”和排列本身一样,求第k个(第k小的)这样的排列。

        也是组合数学的脑洞题。。分析一下可以发现,所谓标准写法,就是只能交换相邻两个数,交换过的数不能再次交换。比如说,最小的合法排列就是1~n的升序,次小的是1~n升序交换最后两个数。

        为了生成答案,我们可以通过若干次交换操作来得到。再分析一下,肯定是优先交换尽可能靠右边的两个数,然后可以惊奇地发现,交换某个位置的数,对大小次序的影响居然和斐波那契数有关。具体比较难描述,见代码。

#include <bits/stdc++.h>using namespace std;#define ll long longint 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;}

C Love Triangles

        有n个人,彼此之间互相love或者hate。对于任意的三个人之间,你不能容忍这两种情况:一是三个人互相hate,二是其中两人hate但是love另外一个人。已知m组关系,问有多少种合法的关系。

        看了一下题解,上面讲了一个结论。如果n个人的关系合法,那么他们肯定组成一个二分图。同一部的人互相love,不同部的人hate。然后我们就可以用带关系的并查集去解决问题。m种已知的关系可能会形成若干的小团体(也可以是独立的个人),固定第一个团体,余下的每个团体都有两种选择。当然已知关系如果已经矛盾了,那答案就是0。

#include <bits/stdc++.h>using namespace std;#define ll long longint 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;}

0 0
原创粉丝点击