20170820考试总结

来源:互联网 发布:天刀捏脸数据女听风 编辑:程序博客网 时间:2024/06/05 17:24

第一题:树 tree

题目描述:已知一棵有根树,树的形态未知,已知树的最大深度为D,深度为i的节点有Ci个,i∈[1,D],求在所有可能的树中直径最长的树直径为多少。D<=10^5

题解:因为树的直径中一定包含一个深度最大的点,枚举其他深度的点,与深度最大的点构成直径,直径长度为它们两点的深度之和减去,在这两个点之前最深的一个Ci=1的点的深度(它们的lca),注意需要枚举到0(根的深度为0)。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=100000+10;int ans,d,num[N],cut[N];int main(){    freopen("tree.in","r",stdin);    freopen("tree.out","w",stdout);    scanf("%d",&d);    int bac=0;    for(int i=1;i<=d;i++){        scanf("%d",&num[i]);        if(num[i]==1) bac=i;        cut[i]=bac;    }    for(int i=0;i<=d;i++){        int res=d+i-(cut[i]<<1);        ans=max(ans,res);    }    printf("%d\n",ans);}

分数:85
分析:忘了考虑根的情况,本来构造了这样的数据,然而当时对深度的定义有点迷。。°(°ˊДˋ°) °。

第二题:酷子集 cool

题目描述:给出n,求一个1-n的集合有多少非空子集满足0-9所有数字出现次数不超过1。e.g:{12,345,67890}和{47,109}是cool子集,{147,342}不是。

题解: dp[S](S是一个10位二进制数,第i位为1表示i以出现过)表示0-9的状态为S时所有合法方案数,cnt[S]表示1-n中单独一个数0-9的状态为S的数有多少个,那么把S拆成两部分a,b(a&b=S)dp[S]=∑dp[a]*cnt[b],但这样是可能计算重复的,那么我们枚举时人为规定b必须包含lowbit(S)就可以避免重复。先dfs求出cnt,然后dp就可以了。(๑•́ ₃ •̀๑)

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<vector>using namespace std;const int N=(1<<10)+10;const int mod=1e9+7;int n,ans,p,End,dp[N],cnt[N];inline void add(int&a,int b){    a+=b;if(a>mod)a-=mod;}int lowbit(int x){    return x&(-x);}void init(int x,int statu,int now){    if(now>0&&now<=n)        cnt[statu]++;    for(int i=0;i<=9;i++){        if(!now&&!i) continue ;        if(1ll*now*10+i>n) break ;        if(!(statu>>i&1))            init(x+1,statu|(1<<i),now*10+i);    }}int main(){    freopen("cool.in","r",stdin);    freopen("cool.out","w",stdout);    scanf("%d",&n);    End=(1<<10)-1;    p=log10(n),init(0,0,0);    dp[0]=1;    for(int now=2;now<=End;now++){        int must=lowbit(now);        for(int i=now;i>0;i=(i-1)&now)            if(i&must){                int a=now-i;                if(!a||!i) continue ;                add(dp[now],dp[a]*cnt[i]);            }        add(dp[now],cnt[now]);        add(ans,dp[now]);    }    printf("%d\n",ans);}

成绩:0
分析:考试时其实想到了正解的一部分,但是一直在纠结怎么判重O__O”…,完全没考虑枚举上的限制,然后就gg了/(ㄒoㄒ)/~~,考完后一听到lowbit感觉世界都明亮了(手动笑哭)

第三题:涂色方案 paint

题目描述:一个2*M的表格,要将其中r个涂成红色,g个涂成绿色,b个涂成蓝色,任意相邻的两个格子颜色不同,每种颜色在每个2 * 2的矩阵里至少出现一次,求方案数,对1e9+7取模。r+g+b=2 * m。m<=10^6

题解:先根据限制和观察,以一列的两个格子为一个单位,rg的旁边必须为br或gb(上下颠倒问题最后*2即可),那么问题变成了,给长度为m的一排格子涂三种颜色,相邻两个格子颜色不同的情况数为多少。看起来很想“湫秋系列故事——安排座位”,然而如果真的使用那道题的做法时间复杂度是O(n^3),显然会T (๑>m<๑) ,换一种考虑,这道题区别于“湫秋系列故事——安排座位”的地方在于它一定只有三种颜色( ⊙o⊙ )(a=rg,b=br,c=gb),那么我们假设先确定了a的位置每两个a之间会有一些空位,一共n-a个,考虑首尾是否为a,间隙个数有a,a-1,a+1三种情况,每两个空位之间的位置个数分为奇,偶两种情况,为偶数时只有bcbc…bc或cbcb…cb两种填法,而奇数的情况为bcb…bcb或cbc…cbc,设共n个位置,cnt个间隙,枚举其中i个间隙的长度为偶数,那么长度为奇数的间隙个数为cnt-i,现在i个偶数间隙中各放入一对b,c(bc,cb两种方法),构成C(pcnt,i) *2^i种情况,再在每个奇数间隙中放入一个b或者c,那么这两不放完之后,还剩下part=((n-i*2)-(cnt-i))/2-i对bc插入前面的间隙构成(相同的小球放入不同的盒子里,盒子可以为空),构成C(part+cnt-1,cnt-1)种情况,全部放完后b还剩下b-part-i个单独的b(c同理)然后这些单独的b和c就放入之前奇数间隙(前面把奇数补成偶数的时候就用这些b和c)构成C(cnt-i,b-part-i)种情况,最后把这三个数乘起来就为当前答案。首为a尾不为a或首不为a尾为a的情况一样,a个间隙的情况算两次,最后答案上下翻转也要*2.

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int mod=1e9+7;const int N=1000000+10;int m,r,g,b,fac[N],inv[N];inline void add(int&a,int b){    a+=b;if(a>=mod)a-=mod;}int qpow(int tmp,int p){    int res=1;    while(p){        if(p&1)res=1ll*res*tmp%mod;        tmp=1ll*tmp*tmp%mod,p>>=1;    }    return res;}int C(int n,int m){    return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}int calc(int cnt,int n,int g,int b){    if(cnt>n||cnt<1) return 0;    int res=0;    for(int i=0;i<=cnt&&i*2<=n;i++){        int least=n-i*2,type=cnt-i;        //剩下的还有least个空余的位置,type个奇数长度的段        if((least-type)&1) continue ;        else if(least<type) continue ;        int part=(least-type)>>1;//剩下的一共还有part个组(B,C)        int G=g-part-i,B=b-part-i;//剩下的B,C还有G,B个单独的        if(G<0||B<0||G+B!=type) continue ;        int tmp=1ll*C(cnt,i)*qpow(2,i)%mod        *C(part+cnt-1,cnt-1)%mod*C(type,G)%mod;        add(res,tmp);    }    return res;}int main(){    freopen("paint.in","r",stdin);    freopen("paint.out","w",stdout);    scanf("%d",&m);    scanf("%d %d %d",&r,&g,&b);    r=m-r,g=m-g,b=m-b;    if(r<0||g<0||b<0)        printf("0\n"),exit(0);    fac[0]=fac[1]=inv[0]=inv[1]=1;    for(int i=2;i<=m;i++){        fac[i]=1ll*fac[i-1]*i%mod;        inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;    }    for(int i=2;i<=m;i++)        inv[i]=1ll*inv[i-1]*inv[i]%mod;    if(r);else if(g)swap(r,g);if(b) swap(r,b);    int ans=0;    add(ans,calc(r-1,m-r,g,b));    add(ans,calc(r+1,m-r,g,b));    add(ans,2ll*calc(r,m-r,g,b)%mod);    (ans*=2)%=mod;    printf("%d\n",ans);}

成绩:0
分析:第二题卡了太久,以至于完全没有时间仔细思考,直到最后才发现用“湫秋系列故事——安排座位”可以骗30分 o(╯□╰)o 2333但是这样的题确实没有见到过类似的(๑´ω`๑),最后那一段感觉特别绕o(@@)o,0分也算正常发挥%><%。

总结:第三题确实很难,然而第一题和第二题都很可惜QnQ,最近几场考试都在考思维(然而全部炸飞Konjak~),而且连续两次第二题卡太久,第三题没有时间(虽然这次第三题很难,但昨天的又水得良心(๑´ω`๑)2333333)。