8.9 考试总结

  集训的时间已经过去了一大半,昨晚化奥的同学的突然出现,对我们而言不仅仅是一种约束,更是结束散漫的生活方式,提高紧张度的一种不那么令人满意的途径。的确,处于集训状态的我们,感觉并没有之前想象的那样紧张高效,虽然适当的放松是有益于更敏捷的思考,但过度的舒适带来的也是精神的松懈。所以在接下来的集训生活里,我们要提高紧张度,让HZOI在2019年焕发出新的光彩。

  今天是八月九日,上午的考试让人喜忧参半。

考试过程:

  依旧还是先大体观察一下题目,发现T1是一道数论,dp有部分分,最多能达到70,正解应该是组合数,可能还有容斥。于是先放在一边,暂且不谈。T2是一道图论,对于图论而言,还是较为擅长,仔细读题,感觉思路异常清晰,决定先切掉,(然而此时并不知道我理解错了题意),欢天喜地地打好了最长链,判断奇偶,分类讨论,瞬间过掉两组样例,觉得考试稳了,然后自己画了一个环,环上有五个点,想验证思路是否正确,于是。。。我把代码删了之后,去上了个厕所,回来疯狂读题,找到正确的思路,在命悬一线之时挽救了自己。T3省略(要不是老师延长了20分钟,可能T3题都没读。。) 

  考试结果:60+100+0=160 rank 6 / 56

考试题解:

  T1:写T1题解前,奉劝大家几句:(扔了40分的血泪教训)

      1、特判记得return 0。

      2、特盘一定要打全。

      3、开数组不要手抖,少打一个0。

  T1是一道数论题,可以视作求m个物品放进n个篮子的方案数,可以想到插板法,即在m个物品中插入n-1个板子,即C(n+m-1,n-1),但由于题目限制,每个篮子中至少有1个,因此可简化为一共只有m-n个物品,此时也出现了第一个特判,如果m<n,则答案为0。那么式子可化简为C(m-1,n-1)。然后考虑另外一个限制条件,每个篮子最多有k个,此时出现第二个特判,如果m>n×k,答案为0。那么我们不妨设有i个篮子,已经达到了k个的限制,即C(m-i×k-1,n-1)。然后我们发现,每个篮子,我们会算重C(n,i)次,可以利用容斥原理处理,于是我们就愉快的拿到了100分的好成绩。

 

 1 #include<bits/stdc++.h>
 2 #define re register
 3 #define ll long long
 4 #define mod 998244353
 5 using namespace std;
 6 ll n,m,k,ans;
 7 ll f[2][10000005],sum[2][10000005];
 8 inline ll read(){
 9     re ll a=0,b=1; re char ch=getchar();
10     while(ch<'0'||ch>'9')
11         b=(ch=='-')?-1:1,ch=getchar();
12     while(ch>='0'&&ch<='9')
13         a=(a<<3)+(a<<1)+(ch^48),ch=getchar();
14     return a*b;
15 }
16 signed main(){
17     n=read(),m=read(),k=read();
18     if(m<n||m>n*k){puts("0");return 0;}
19     for(re int i=m-n-k+1;i<=m-n;++i) f[1][i]=1;
20     for(re int i=m-n;~i;--i)
21         sum[1][i]=sum[1][i+1]+f[1][i];
22     for(re ll i=2;i<=n;++i){
23         sum[i&1][m-n+1]=0;
24         for(re int j=m-n;~j;--j)
25             f[i&1][j]=(sum[i&1^1][j]-sum[i&1^1][min(m-n+1,j+k)]+mod)%mod,
26             sum[i&1][j]=(sum[i&1][j+1]+f[i&1][j])%mod;
27     }
28     ans=f[n&1][0];
29     printf("%lld\n",ans%mod);
30 }
70分DP

 

 1 #include<bits/stdc++.h>
 2 #define re register
 3 #define mod 998244353
 4 #define int long long
 5 using namespace std;
 6 int n,m,k,fac[10000001],inv[10000001],ans;
 7 inline int read(){
 8     re int a=0,b=1; re char ch=getchar();
 9     while(ch<'0'||ch>'9')
10         b=(ch=='-')?-1:1,ch=getchar();
11     while(ch>='0'&&ch<='9')
12         a=(a<<3)+(a<<1)+(ch^48),ch=getchar();
13     return a*b;
14 }
15 inline int qpow(re int x,re int y,re int res=1){
16     for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;
17     return res;
18 }
19 inline void init(){
20     fac[0]=fac[1]=1;
21     for(re int i=1;i<=m;++i) 
22         fac[i]=fac[i-1]*i%mod,inv[i]=qpow(fac[i],mod-2)%mod;
23 }
24 inline int C(re int x,re int y){
25     if(x==y)return 1;
26     return fac[x]*inv[y]%mod*inv[x-y]%mod;
27 }
28 signed main(){
29     n=read(),m=read(),k=read();
30     if(m<n||m>n*k){puts("0");return 0;}
31     init();
32     ans=C(m-1,n-1);
33     for(re int i=1;i<=n;++i){
34         if(m<n+k*i)continue;
35         re int res=C(n,i);
36         re int tmp=C(m-i*k-1,n-1);
37         ans+=(qpow(-1,i)*res%mod*tmp%mod)%mod;
38         ans=(ans+mod)%mod;
39     }
40     printf("%lld\n",ans);
41 }
100分组合数学

 

 

  T2:算法流程:tarjan缩点+拓扑排序。

  观察题面,我们发现,对于一个环,我们需要操作的次数即为这个环上的点数,因为一个环是一个强联通子图,彼此可以互相到达,因此只能每次对一个节点操作,才能保证符合题意。所以我们可以对每一个强联通分量缩点,并记录点数,以点数为点权,重新建图,那么我们该如何处理联通的节点之间的操作方式呢?容易想到拓扑排序来处理点与点之间的关系,所以我们随着拓扑不断更新权值,得到的即为最终答案。

 

 1 #include<bits/stdc++.h>
 2 #define re register
 3 using namespace std;
 4 int n,m,tot=0,first[1000010],second[1000010],tot2=0,cnt=0,num=0;
 5 int dfn[1000010],low[1000010],bel[1000010],scc[1000010],in[1000010];
 6 bool ins[1000010];int dis[1000010],ans;
 7 struct node{int u,v,next;}edge[1000010],road[1000010];
 8 inline int read(){
 9     re int a=0,b=1; re char ch=getchar();
10     while(ch<'0'||ch>'9')
11         b=(ch=='-')?-1:1,ch=getchar();
12     while(ch>='0'&&ch<='9')
13         a=(a<<3)+(a<<1)+(ch^48),ch=getchar();
14     return a*b;
15 }
16 inline void add(re int x,re int y){
17     edge[++tot].v=y;
18     edge[tot].u=x;
19     edge[tot].next=first[x];
20     first[x]=tot;
21 }
22 inline void add2(re int x,re int y){
23     road[++tot2].v=y;
24     road[tot2].u=x;
25     road[tot2].next=second[x];
26     second[x]=tot2;
27 }
28 stack<int> s;
29 inline void tarjan(re int x){
30     dfn[x]=low[x]=++num;
31     ins[x]=1;s.push(x);
32     for(re int i=first[x];i;i=edge[i].next){
33         re int y=edge[i].v;
34         if(!dfn[y]){
35             tarjan(y);
36             low[x]=min(low[x],low[y]);
37         }
38         else if(ins[y])
39             low[x]=min(low[x],dfn[y]);
40     }
41     if(low[x]==dfn[x]){
42         ++cnt;re int res;
43         do{
44             res=s.top();s.pop();
45             ins[res]=0,bel[res]=cnt;
46             scc[cnt]++;
47         }while(res!=x);
48     }
49 }
50 signed main(){
51     n=read(),m=read();
52     for(re int i=1,a,b;i<=m;++i){
53         a=read(),b=read();
54         add(a,b);
55     }
56     for(re int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
57     for(re int i=1;i<=tot;++i)
58         if(bel[edge[i].u]!=bel[edge[i].v]){
59             add2(bel[edge[i].u],bel[edge[i].v]);
60             in[bel[edge[i].v]]++;
61         }
62     queue<int> q;
63     for(re int i=1;i<=cnt;++i){
64         dis[i]=scc[i];
65         if(!in[i]) q.push(i);
66     }
67     while(q.size()){
68         re int x=q.front();q.pop();
69         for(re int i=second[x];i;i=road[i].next){
70             re int y=road[i].v;--in[y];
71             if(dis[y]<dis[x]+scc[y]) dis[y]=dis[x]+scc[y];
72             if(!in[y]) q.push(y);
73         }
74     }
75     for(re int i=1;i<=cnt;++i) ans=max(ans,dis[i]);
76     printf("%d\n",ans);
77 }
T2

 

   

  T3:   

  用 g[i][j][k] 表示前 i+j+k 轮出了 i 个石头,j 个剪刀,k个布的概率,f[i][j][k][l]表示前 i+j+k 轮出了 i 个石头,j个剪刀,k 个布,下一轮出 l 的概率。
枚举i,j,k对 f[i][j][k][x]+f[i][j][k][x+1]×3 取个 max,然后累加起来就是答案。
  我们考虑如何求出 g。前 x 轮情况总数为 300^x,我们只要求出 i 个石头,j 个剪刀,k 个布对应的方案数即可。
  转移方程为 g[i][j][k]+=g[i-1][j][k]*r[t]+g[i][j-1][k]*s[t]+g[i][j][k-1]*p[t]。(t 是枚举每一个人,这一维被压掉了,最后统计答案时只需要使用 g[n][i][j][k])
f 的转移也是类似的。
  注意输入的顺序为石头、布、剪刀。

 

 1 #include<bits/stdc++.h>
 2 #define re register
 3 #define ff f[i][j][k]
 4 using namespace std;
 5 int n;double r[101],p[101],s[101],c[101][101];
 6 double f[101][101][101][4],g[101][4],ans;
 7 inline int read(){
 8     re int a=0,b=1; re char ch=getchar();
 9     while(ch<'0'||ch>'9')
10         b=(ch=='-')?-1:1,ch=getchar();
11     while(ch>='0'&&ch<='9')
12         a=(a<<3)+(a<<1)+(ch^48),ch=getchar();
13     return a*b;
14 }
15 signed main(){
16     n=read();f[0][0][0][0]=1;
17     for(re int i=0;i<=n;++i){
18         c[i][0]=1;
19            for(re int j=1;j<=i;++j)
20             c[i][j]=c[i-1][j]+c[i-1][j-1];
21     }
22     for(re int i=1;i<=n;++i)
23         r[i]=(double)read()/300.0,p[i]=(double)read()/300.0,s[i]=(double)read()/300.0;
24     for(re int i=1;i<=n;++i) g[i][1]=r[i],g[i][2]=s[i],g[i][3]=p[i];
25     for(re int l=1;l<=n;++l) for(re int i=l;~i;--i)
26     for(re int j=l-i;~j;--j) for(re int k=l-i-j;~k;--k)
27     for(re int u=(i+j+k==l?0:3);u>=0;--u){
28         if(i) ff[u]+=f[i-1][j][k][u]*g[l][1];
29         if(j) ff[u]+=f[i][j-1][k][u]*g[l][2];
30         if(k) ff[u]+=f[i][j][k-1][u]*g[l][3];
31         if(u) ff[u]+=f[i] [j][k] [0]*g[l][u];
32     }    
33     for(re int i=0;i<n;++i) for(re int j=0;i+j<n;++j) for(re int k=0;i+j+k<n;++k)
34     ans+=max(ff[1]+3*ff[2],max(ff[2]+3*ff[3],ff[3]+3*ff[1]))/(c[n][i+j+k]*(n-i-j-k));
35     printf("%.12lf",ans);
36     return 0;
37 }
填坑

 

posted @ 2019-08-09 14:42  Hzoi-lyl  阅读(224)  评论(1编辑  收藏  举报