Codeforces #309 Div 1 简要题解

来源:互联网 发布:js div height 编辑:程序博客网 时间:2024/05/21 11:09

A. Kyoya and Colored Balls

题目链接

http://codeforces.com/contest/553/problem/A

题目大意

k种颜色的球,每种ci个,要求第i种球的最后一个球要在第i+1种球的最后一个球之前放置。问有多少种合法的放置球的方案。

思路

我们可以初始先在这个放置序列里填入每种颜色最后一个球,然后从1号球到k号球,填入每种球,ci1个第i个球都必须填入到第i个球的最后一个球之前

初始时只有一种方案:
1 2 3 4… k
然后我们放入c11个1号球,方案数为Cc11c11
1111 2 3 4 … k
然后我们放入c21个1号球,方案数为Cc21c1+c21
……

也就是说,填入第i号球时,方案数为

CCi1(Ci1)i()

我们在统计答案的过程中维护Ci的前缀和来对这个做法进行优化

代码

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXN 1100#define MOD 1000000007using 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…,从某些相邻的元素交换而得到的,而且不存在aiaj交换,而aj又和ak交换的情况。
证明窝不会。。。但是可以自己找规律发现这个问题

那么我们可以得到一个dp方程:f[i]=长度为i的Kyoya’s permutation个数。则f[i]=f[i1]+f[i2](第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,ni]]里的,假设第i个元素选择与第i+1个元素交换,那么得到的序列的i~n部分在所有的数字i,i+1…n的排列里的字典序编号是在[fib[ni]+1,fib[ni+1]]里的。这个请自己仔细研究搞明白。

如果K>fib[ni](这个K是答案序列的i~n部分在所有的数字i,i+1…n的排列里的字典序编号),很显然就说明之后i+1~n号元素的排列就算是字典序编号最大的Kyoya’s permutation,整个序列的字典序编号也会小于K,那么就需要交换i和i+1,K-=fib[ni],然后再去求答案序列里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 1000000007using 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;}
0 0