noip模拟测试15


T1:建设城市(city)

  第一眼看是组合,然后看到k的限制发现是容斥

  用插板法加容斥得出:$\sum_{i=0}^{m-i*k-1 \leq n-1}C_n^i*C_{m-i*k-1}^{n-1}*(-1)^i$

  但发现$n$的范围是$10^9$,组合数计算是$O(n)$的

  但又发现$m$的范围是$10^7$,所以特判$n>m$的情况就行了

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<vector>
 7 #include<queue>
 8 #define ll long long
 9 using namespace std;
10 const int MAXN=10000005;
11 const ll D=998244353;
12 ll n,m,k,ans,fac[MAXN],inv[MAXN];
13 ll qpow(ll x,ll k) {
14     ll ret=1;
15     while(k) {
16         if(k&1) ret=ret*x%D;
17         x=x*x%D;
18         k>>=1;
19     }
20     return ret;
21 }
22 void first() {
23     fac[0]=inv[0]=1;
24     for(int i=1;i<=m;i++)
25         fac[i]=fac[i-1]*i%D;
26     inv[m]=qpow(fac[m],D-2);
27     for(int i=m-1;i>=1;i--)
28         inv[i]=inv[i+1]*(i+1)%D;
29 }
30 ll C(int x,int y) {
31     return fac[x]*inv[x-y]%D*inv[y]%D;
32 }
33 int main() {
34     scanf("%lld%lld%lld",&n,&m,&k);
35     if(n>m||m>n*k) {
36         printf("0\n");
37         return 0;
38     } else if(n==m||m==n*k) {
39         printf("1\n");
40         return 0;
41     }
42     first();
43     ans=C(m-1,n-1);
44     for(ll i=1;;i++) {
45         if(m-i*k-1<n-1) break;
46         if(i&1) ans=(ans-C(n,i)*C(m-i*k-1,n-1))%D;
47         else ans=(ans+C(n,i)*C(m-i*k-1,n-1))%D;
48     }
49     printf("%lld\n",(ans%D+D)%D);
50     return 0;
51 }
t1 Code

 


T2: 轰炸行动(bomb)

  读题出锅……

  就是简单的tarjan,缩完scc后跑个拓扑求点权的最长链就行了

  自己手玩几组数据就知道了

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<vector>
 7 #include<queue>
 8 #include<bitset>
 9 #define ll long long
10 using namespace std;
11 const int MAXN=1100000,INF=0x3f3f3f3f;
12 int n,m,ans,dfn[MAXN],low[MAXN],stk[MAXN],tp,num;
13 int scc_cnt,scc[MAXN],siz[MAXN],du[MAXN],f[MAXN];
14 bool vis[MAXN];
15 struct edge {
16     int x,y;
17 }e[MAXN];
18 struct node {
19     int to,nxt;
20 }mp[MAXN];
21 int h[MAXN],tot;
22 void add(int x,int y) {
23     mp[++tot].to=y;
24     mp[tot].nxt=h[x];
25     h[x]=tot;
26 }
27 void tarjan(int u) {
28     dfn[u]=low[u]=++num;
29     stk[++tp]=u;vis[u]=1;
30     for(int i=h[u];i;i=mp[i].nxt) {
31         int v=mp[i].to;
32         if(!dfn[v]) {
33             tarjan(v);
34             low[u]=min(low[u],low[v]);
35         } else if(vis[v]) low[u]=min(low[u],dfn[v]);
36     }
37     if(dfn[u]==low[u]) {
38         ++scc_cnt;
39         int tmp;
40         do {
41             tmp=stk[tp--];
42             vis[tmp]=0;
43             siz[scc_cnt]++;
44             scc[tmp]=scc_cnt;
45         }while(tmp!=u);
46     }
47 }
48 void build() {
49     memset(h,0,sizeof(h));tot=0;
50     for(int i=1;i<=m;i++)
51         if(scc[e[i].x]!=scc[e[i].y]) {
52             add(scc[e[i].x],scc[e[i].y]);
53             du[scc[e[i].y]]++;
54         }
55 }
56 void topo() {
57     queue<int> q;
58     for(int i=1;i<=scc_cnt;i++)
59         if(!du[i]) q.push(i);
60     while(!q.empty()) {
61         int u=q.front(); q.pop();
62         f[u]+=siz[u];
63         for(int i=h[u];i;i=mp[i].nxt) {
64             int v=mp[i].to;
65             f[v]=max(f[v],f[u]);
66             if(!--du[v]) q.push(v);
67         }
68     }
69 }
70 int main() {
71     scanf("%d%d",&n,&m);
72     for(int i=1;i<=m;i++) {
73         scanf("%d%d",&e[i].x,&e[i].y);
74         add(e[i].x,e[i].y);
75     }
76     for(int i=1;i<=n;i++)
77         if(!dfn[i]) tarjan(i);
78     build();
79     topo();
80     for(int i=1;i<=scc_cnt;i++) ans=max(ans,f[i]);
81     printf("%d\n",ans);
82     return 0;
83 }
t2 Code

 


 

T3:石头剪刀布(rps)

  懵比……

  设计$g[t][i][j][k]$表示前t个人,出i个石头,j个布子,k个剪刀的概率

  则$g[t][i][j][k]=g[t-1][i][j][k]+g[t-1][i-1][j][k]*r[t]+g[t-1][i][j-1][k]*p[t]+g[t-1][i][j][k-1]*s[t]$

  即上一层出i,j,k的概率加上少出一个石头/布子/剪刀,然后这次再出一个的概率

  再设计$f[i][j][k][0/1/2]$表示前i+j+k次中出i个石头,j个布子,k个剪刀,且下一次会出0/1/2->石头/布子/剪刀的概率

  则$f[i][j][k][0]=f[i-1][j][k][0]*r[t]+f[i][j-1][k][0]*p[t]+f[i][j][k-1][0]*s[t]+g[t-1][i][j][k][0]*r[t]$

   $f[i][j][k][1]=f[i-1][j][k][1]*r[t]+f[i][j-1][k][1]*p[t]+f[i][j][k-1][1]*s[t]+g[t-1][i][j][k][1]*p[t]$

   $f[i][j][k][2]=f[i-1][j][k][2]*r[t]+f[i][j-1][k][2]*p[t]+f[i][j][k-1][2]*s[t]+g[t-1][i][j][k][2]*s[t]$

  即:分别考虑新来的这个人t在合适出来,打出什么

   第一个式子中:

   $f[i-1][j][k][0]*r[t]+f[i][j-1][k][0]*p[t]+f[i][j][k-1][0]*s[t]$

    表示t这个人在猜拳序列的中间出现,则对下一次出什么没有影响

   $g[t-1][i][j][k-1]*s[t]$

    表示t这个人在序列的最后出现,则与前面人怎么出无关,而与它出什么有关

   后两个式子也是一样的

  最后统计答案时枚举$i,j,k(i+j+k<n)$

  答案为$\sum max (3*f[i][j][k][0/1/2]+f[i][j][k][1/2/0]) / (C_n^{i+j+k} * (n-i-j-k))$

  即:分别决策每种情况出什么最优,将最大期望得分除以总方案数累加起来

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<vector>
 7 #include<queue>
 8 #include<bitset>
 9 #define ld long double
10 using namespace std;
11 const int MAXN=55;
12 int n;
13 ld C[MAXN][MAXN];
14 ld r[MAXN],p[MAXN],s[MAXN],ans;
15 ld f[MAXN][MAXN][MAXN][3],g[MAXN][MAXN][MAXN][MAXN];
16 int main() {
17     scanf("%d",&n);
18     for(int i=1;i<=n;i++) scanf("%Lf%Lf%Lf",&r[i],&p[i],&s[i]);
19     for(int i=1;i<=n;i++) {
20         r[i]/=300.0;
21         p[i]/=300.0;
22         s[i]/=300.0;
23     }
24     for(int i=0;i<=n;i++) {
25         C[i][0]=1;
26         for(int j=1;j<=i;j++)
27             C[i][j]=C[i-1][j]+C[i-1][j-1];
28     }
29     g[0][0][0][0]=1;
30     for(int t=1;t<=n;t++)
31         for(int i=t;i>=0;i--)
32             for(int j=t-i;j>=0;j--)
33                 for(int k=t-i-j;k>=0;k--) {
34                     g[t][i][j][k]=g[t-1][i][j][k];
35                     if(i)g[t][i][j][k]+=g[t-1][i-1][j][k]*r[t];
36                     if(j)g[t][i][j][k]+=g[t-1][i][j-1][k]*p[t];
37                     if(k)g[t][i][j][k]+=g[t-1][i][j][k-1]*s[t];
38                 }
39     for(int t=1;t<=n;t++)
40         for(int i=t;i>=0;i--)
41             for(int j=t-i;j>=0;j--)
42                 for(int k=t-i-j;k>=0;k--) {
43                     if(i) {
44                         f[i][j][k][0]+=f[i-1][j][k][0]*r[t];
45                         f[i][j][k][1]+=f[i-1][j][k][1]*r[t];
46                         f[i][j][k][2]+=f[i-1][j][k][2]*r[t];
47                     }
48                     if(j) {
49                         f[i][j][k][0]+=f[i][j-1][k][0]*p[t];
50                         f[i][j][k][1]+=f[i][j-1][k][1]*p[t];
51                         f[i][j][k][2]+=f[i][j-1][k][2]*p[t];
52                     }
53                     if(k) {
54                         f[i][j][k][0]+=f[i][j][k-1][0]*s[t];
55                         f[i][j][k][1]+=f[i][j][k-1][1]*s[t];
56                         f[i][j][k][2]+=f[i][j][k-1][2]*s[t];
57                     }
58                     f[i][j][k][0]+=g[t-1][i][j][k]*r[t];
59                     f[i][j][k][1]+=g[t-1][i][j][k]*p[t];
60                     f[i][j][k][2]+=g[t-1][i][j][k]*s[t];
61                 }
62     for(int i=0;i<n;i++)
63         for(int j=0;i+j<n;j++)
64             for(int k=0;i+j+k<n;k++) {
65                 ld tmp=0.0;
66                 tmp=f[i][j][k][0]*3+f[i][j][k][1];
67                 tmp=max(tmp,f[i][j][k][1]*3+f[i][j][k][2]);
68                 tmp=max(tmp,f[i][j][k][2]*3+f[i][j][k][0]);
69                 ans+=tmp/(C[n][i+j+k]*(n-i-j-k));
70             }
71     printf("%.12Lf\n",ans);
72     return 0;
73 }
t3 Code

 

   

转载于:https://www.cnblogs.com/Gkeng/p/11326961.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值