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 }
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 }
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 }
来源:https://www.cnblogs.com/Gkeng/p/11326961.html
