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 "); 37 return 0; 38 } else if(n==m||m==n*k) { 39 printf("1 "); 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 ",(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 ",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 ",ans); 72 return 0; 73 }