A. Kyoya and Colored Balls
题目链接
http://codeforces.com/contest/553/problem/A
题目大意
有
k
种颜色的球,每种
思路
我们可以初始先在这个放置序列里填入每种颜色最后一个球,然后从1号球到k号球,填入每种球, ci−1 个第i个球都必须填入到第i个球的最后一个球之前
初始时只有一种方案:
1 2 3 4… k
然后我们放入
c1−1
个1号球,方案数为
Cc1−1c1−1
:
1111 2 3 4 … k
然后我们放入
c2−1
个1号球,方案数为
Cc2−1c1+c2−1
……
也就是说,填入第
i
号球时,方案数为
我们在统计答案的过程中维护 Ci 的前缀和来对这个做法进行优化
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100
#define MOD 1000000007
using namespace std;
typedef long long int LL;
LL C[MAXN][MAXN];
LL fastPow(LL base,LL pow)
{
LL ans=1;
while(pow)
{
if(pow&1) ans=ans*base%MOD;
base=base*base%MOD;
pow>>=1;
}
return ans;
}
int K;
LL sum=0;
int main()
{
C[0][0]=1;
for(int i=1;i<MAXN;i++)
for(int j=0;j<=i;j++)
{
if(j==0||j==i) C[i][j]=1;
else C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
}
LL ans=1;
scanf("%d",&K);
for(int i=1;i<=K;i++)
{
int x;
scanf("%d",&x);
sum+=x;
ans=ans*C[sum-1][x-1]%MOD;
}
printf("%I64d\n",ans);
return 0;
}
B. Kyoya and Permutation
题目链接
http://codeforces.com/contest/553/problem/B
题目大意
给你一个置换的加法,如 (142)(36)(5),置换得到的序列是[4, 1, 6, 2, 5, 3]
定义一个置换加法式子的cyclic representation是将每个置换环把最大的数字放在环的最前面,去掉所有的括号后得到的序列,如 (142)(36)(5)<=>(421)(63)(5)–>421635
定义Kyoya’s permutation为cyclic representation和置换得到的序列相同的序列,求长度为n的字典序第 k 小的Kyoya’s permutation
思路
首先要知道一个结论,所有的Kyoya’s permutation,都是由排列12345…,从某些相邻的元素交换而得到的,而且不存在
证明窝不会。。。但是可以自己找规律发现这个问题
那么我们可以得到一个dp方程:
f[i]=
长度为i的Kyoya’s permutation个数。则
f[i]=f[i−1]+f[i−2]
(第i个元素要么选择不动,方案数f[i-1],要么选择和i-1号元素进行交换,方案数f[i-2])
初始f[1]=1,f[2]=2,这是什么?fibonacci数!
然后剩下的问题比较像数位DP了,告诉你每个长度的Kyoya’s permutation个数,求出第k个Kyoya’s permutation。
显然对于i和i+1号元素,其他部分相同的序列A和B,若A的这两个元素没交换,B的这两个元素交换了,那么A在字典序里就小于B。这个性质在下面会得到充分利用
我们可以这样:从左到右枚举Kyoya’s permutation的下标i,初始时序列为12345…,假设第i个元素不选择与第i+1个元素交换,那么得到的序列的i~n部分在所有的数字i,i+1…n的排列里的字典序编号是在 [1,fib[1,n−i]] 里的,假设第i个元素选择与第i+1个元素交换,那么得到的序列的i~n部分在所有的数字i,i+1…n的排列里的字典序编号是在 [fib[n−i]+1,fib[n−i+1]] 里的。这个请自己仔细研究搞明白。
如果 K>fib[n−i] (这个K是答案序列的i~n部分在所有的数字i,i+1…n的排列里的字典序编号),很显然就说明之后i+1~n号元素的排列就算是字典序编号最大的Kyoya’s permutation,整个序列的字典序编号也会小于K,那么就需要交换i和i+1,K-= fib[n−i] ,然后再去求答案序列里i+2~n那部分。
其实这个过程也很像平衡树查询第k小数,思想上差不多,大家自己研究透彻就能发现其中的异曲同工之妙了
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long int LL;
int n;
LL K;
int ans[100];
LL fib[100];
int main()
{
scanf("%d%I64d",&n,&K);
fib[0]=fib[1]=1,fib[2]=2;
for(int i=3;i<=n;i++)
fib[i]=fib[i-1]+fib[i-2];
for(int i=1;i<=n;i++) ans[i]=i;
for(int i=1;i<=n;i++)
if(K>fib[n-i])
{
K-=fib[n-i];
swap(ans[i],ans[i+1]);
i++;
}
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
return 0;
}
C. Love Triangles
题目链接
http://codeforces.com/contest/553/problem/C
题目大意
给出一个n个点的无向完全图,图中每条无向边要么是红色要么是蓝色,并给出其中m条事先已经染好色的边的颜色。定义这个图的一种边的染色是完美的, ∀i,j,k ,三点之间连接的三条边,要么是红,蓝,蓝;要么是红,红,红。问有多少种完美的染色方案。
思路
假设已经知道了边i-j的颜色,那么对于点1,i,j而言:
若i-j是红色,那么1-i和1-j要么都是红色,要么都是蓝色
若i-j是红色,那么1-i和1-j中一个是红色,另一个是蓝色
因此只要知道i-j,就可以知道1-i与1-j的关系了。若i-j为红色,则1-i和1-j同色;若i-j为蓝色,则1-i和1-j异色。而1连出的边共有n-1条,我们就能维护一个n-1个点的种类并查集。对于每个已经知道颜色的边i-j,根据边的颜色来合并i和j所在的联通块,若i和j已经在同一联通块里,而现在又出现了种类上的冲突,就可以判定为无解。
对于最后的并查集里每个联通块,都有2种完全截然相反的染色方法,假设联通块有t个,那么染色方案数为 2t
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXV 110000
#define MAXE 110000
#define MOD 1000000007
using namespace std;
typedef long long int LL;
int f[MAXV],col[MAXV];
int n,m;
int findSet(int x)
{
if(f[x]==x) return x;
int tmp=findSet(f[x]);
col[x]^=col[f[x]]; //!!!!一开始col[x]是x和其父亲的相对关系(1表示相反类型,0表示相同类型),当父亲的颜色变化后,对col[x]亦或上父亲的颜色即可(若其与父亲关系相反,那么亦或后颜色会和父亲相反,反之亦或后和父亲颜色相同)
f[x]=tmp;
return f[x];
}
LL ans=1;
int tot=0; //并查集联通块的个数
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) f[i]=i;
tot=n-1;
for(int i=1;i<=m;i++)
{
int u,v,color;
scanf("%d%d%d",&u,&v,&color); //边u-v是红色(1),则u和v相同,否则相反
color^=1;
int rootu=findSet(u),rootv=findSet(v);
if(!ans) continue;
if(rootu!=rootv)
{
col[rootu]=col[u]^col[v]^color;
f[rootu]=rootv;
tot--;
}
else
{
if((col[u]^col[v]^color)!=0)
ans=0;
}
}
for(int i=1;i<=tot;i++) ans=ans*2%MOD;
printf("%I64d\n",ans);
return 0;
}