总结
第二次比赛在配合与节奏上稍微比第一次有了较大的进步,所谓一回生二回熟,大概就是这样吧。但是,问题同样很明显,首先就我而言1、我打题的准确率不够高,一道题要重复交好几遍,即使我知道这只是碰运气……2、算法能力较差,有点拖累了队友的感觉 3、代码实现能力仍有待加强
Rikka with Nash Equilibrium (dp)
description
给定n,m(<80),求一个满足纳什平衡点只有一个的矩形,一个点是纳什平衡当且仅当:
solution
看到题目dp走起,我们从大到小开始放数,设f[k][i][j]表示当前排了i行j列,占用了其中k个位置,f[k][i][j]可转移至f[k+1][i][j],f[k+1][i+1][j],f[k+1][i][j+1]分别表示放入原矩阵i*j,新开一行或新开一列,由于都是从大到小放,而且新开得到行列都基于原来的行列,故纳什平衡点不会增加
code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rp(i,a,b) for(int i=a;i>=b;i--)
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define ll long long
using namespace std;
ll dp[85][85][2];
int main()
{
ll n,m,mo;
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld%lld",&n,&m,&mo);
memset(dp,0,sizeof(dp));
dp[1][1][1]=n*m%mo;
int flag=1;
ll ans=0;
for(ll k=2;k<=n*m;k++)
{
flag=1-flag;
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=m;j++)
{
dp[i][j][flag]=0;
if(k>i*j)continue;
dp[i][j][flag]=dp[i][j][1-flag]*(i*j-k+1)%mo;
dp[i][j][flag]=(dp[i][j][flag]+dp[i-1][j][1-flag]*(n-i+1)%mo*j)%mo;
dp[i][j][flag]=(dp[i][j][flag]+dp[i][j-1][1-flag]*(m-j+1)%mo*i)%mo;
if(k==n*m)ans=(ans+dp[i][j][flag])%mo;
}
}
}
printf("%lld\n",ans);
}
}
B Rikka with Seam (dp)
description
给出一个n*m(<2e3)的01矩阵,要求计算每行选择一个位置删除后构成的不同矩阵的数量,相邻行选择删除的位置不能相差k
solution
设f[i][j]表示删除i行第j个数造成的不同矩阵的个数,g[i][j]表示删除i行第j个数造成的与删除第j-1个数造成的01矩阵完全相同的数量,当a[i][j]!=a[i][j-1]时,g[i][j]=0。 f [ i ] [ j ] = ∑ k = j − K j + K f [ i − 1 ] [ k ] − ∑ k = j − K + 1 j + K g [ i − 1 ] [ k ] f[i][j]=\sum_{k=j-K}^{j+K}f[i-1][k]-\sum_{k=j-K+1}^{j+K}g[i-1][k] f[i][j]=k=j−K∑j+Kf[i−1][k]−k=j−K+1∑j+Kg[i−1][k] g [ i ] [ j ] = ∑ k = j − K j + K − 1 f [ i − 1 ] [ k ] − ∑ k = j − K + 1 j + K − 1 g [ i − 1 ] [ k ] g[i][j]=\sum_{k=j-K}^{j+K-1}f[i-1][k]-\sum_{k=j-K+1}^{j+K-1}g[i-1][k] g[i][j]=k=j−K∑j+K−1f[i−1][k]−k=j−K+1∑j+K−1g[i−1][k]前缀和优化一下即可。
code
#include<bits/stdc++.h>
using namespace std;
const int mo=998244353;
int t,n,m,k;
char s[2010][2010];
long long dp1[2010][2010],dp2[2010][2010];
int main(){
for(int i=0;i<=2000;i++) dp1[1][i]=i;
for(int i=1;i<=2000;i++) dp1[i][0]=dp2[i][0]=0;
cin>>t;
while(t--){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%s",s[i]);
for(int i=1;i<m;i++)
if(s[1][i]==s[1][i-1]) dp2[1][i+1]=1;
else dp2[1][i+1]=0;
dp2[1][1]=0;
for(int i=1;i<=m;i++) dp2[1][i]+=dp2[1][i-1];
for(int i=2;i<=n;i++){
for(int j=1;j<=m;j++){
dp1[i][j]=(dp1[i-1][min(j+k,m)]-dp1[i-1][max(j-k-1,0)]-(dp2[i-1][min(j+k,m)]-dp2[i-1][max(j-k,0)])+2*mo)%mo;
if(j>=2&&s[i][j-1]==(s[i][j-2])) dp2[i][j]=(dp1[i-1][min(j+k-1,m)]-dp1[i-1][max(j-k-1,0)]-(dp2[i-1][min(j+k-1,m)]-dp2[i-1][max(j-k,0)])+2*mo)%mo;
else dp2[i][j]=0;
}
for(int j=1;j<=m;j++){
dp1[i][j]=(dp1[i][j]+dp1[i][j-1]+mo)%mo;
dp2[i][j]=(dp2[i][j-1]+dp2[i][j]+mo)%mo;
}
}
long long ans=(dp1[n][m]-dp2[n][m]+mo)%mo;
cout<<ans<<endl;
}
return 0;
}
D Rikka with Stone-Paper-Scissors (数学题 结论题)
description
有两个人在玩石头剪刀布,小A有a,b,c个剪刀石头布,小B有A,B,C个剪刀石头布(a+b+c=A+B+C),小A每次随机出,小B可以透视到小A目前有的牌后选择性的出牌。每轮赢者得一分,输者扣一分,问小B的期望得分。
solution
不知道那群大哥是怎么随手写结论的,反正awsl,设f[n][a][b][A][B]表示当前情况的期望,我们假设c最大,那么小B每次肯定出A
f
[
n
]
[
a
]
[
b
]
[
A
]
[
B
]
=
a
n
f
[
n
−
1
]
[
a
−
1
]
[
b
]
[
A
−
1
]
[
B
]
+
b
n
f
[
n
−
1
]
[
a
]
[
b
−
1
]
[
A
−
1
]
[
B
]
+
c
n
f
[
n
−
1
]
[
a
]
[
b
]
[
A
−
1
]
[
B
]
+
c
−
b
n
f[n][a][b][A][B]=\frac{a}{n}f[n-1][a-1][b][A-1][B]+\frac{b}{n}f[n-1][a][b-1][A-1][B]+\frac{c}{n}f[n-1][a][b][A-1][B]+\frac{c-b}{n}
f[n][a][b][A][B]=naf[n−1][a−1][b][A−1][B]+nbf[n−1][a][b−1][A−1][B]+ncf[n−1][a][b][A−1][B]+nc−b
我们假设
f
[
n
]
[
a
]
[
b
]
[
A
]
[
B
]
=
A
(
c
−
b
)
+
B
(
a
−
c
)
+
C
(
b
−
a
)
n
f[n][a][b][A][B]=\frac{A(c-b)+B(a-c)+C(b-a)}{n}
f[n][a][b][A][B]=nA(c−b)+B(a−c)+C(b−a)(别问我怎么来的 ),那么f[1]成立,若f[n]成立,推得f[n+1]也成立,故结论成立
code
#include<bits/stdc++.h>
using namespace std;
const long long mo=998244353;
long long gcd(long long x,long long y){
return y?gcd(y,x%y):x;
}
int main(){
long long q,n,m,t,a,b,c,a1,b1,c1;
cin>>t;
while(t--){
cin>>a>>b>>c>>a1>>b1>>c1;
n=a1*(c-b)+b1*(a-c)+c1*(b-a);
m=a+b+c;
q=gcd(abs(n),m);
n/=q;
m/=q;
if(m==1||n==0) cout<<n<<endl;
else cout<<n<<"/"<<m<<endl;
}
return 0;
}
J Rikka with Time Complexity(模拟)
description
Let fa(n)=log…logn (there are exactly a log in this function, and log uses base 2). And then, for an integer array A, Rikka defines gA(n) in the following way (B is the suffix of A with length |A|−1):
For example,
g
[
1
,
2
]
(
n
)
=
(
l
o
g
n
)
l
o
g
l
o
g
n
g[1,2](n)=(logn)^{loglogn}
g[1,2](n)=(logn)loglogn and
g
[
3
,
1
,
1
]
(
n
)
=
(
l
o
g
l
o
g
l
o
g
n
)
l
o
g
n
l
o
g
n
g[3,1,1](n)=(logloglogn)^{logn^{logn}}
g[3,1,1](n)=(logloglogn)lognlogn.
Now, given integer arrays A and B, Rikka wants you to compare gA(n) with gB(n). i.e., let k be limn→+∞gA(n)gB(n). If k=0, output −1; if k=+∞, output 1; otherwise output 0.m<=2000)( the length of A and B. <=3)
solution
我们发现 l o g g A ( n ) = g B ( n ) ∗ f A 1 + 1 ( n ) logg_A(n)=g_B(n)*f_{A1+1}(n) loggA(n)=gB(n)∗fA1+1(n)那就很舒服了,我们直接用这种方式展开式子后从小到大排序比较即可。
code
#include<bits/stdc++.h>
using namespace std;
const int N=5;
int read(){
int f=1,s=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='0')f=-1;
for(;c>='0'&&c<='9';c=getchar())s=s*10+c-48;
return f*s;
}
const int oo=0x3f3f3f3f;
struct pp{
int x,y;
pp(int x_,int y_){
x=x_;y=y_;
if(x>y)swap(x,y);
}
};
int cmp(pp a,pp b){
if(a.x<b.x)return 1;
if(a.x>b.x)return -1;
if(a.y<b.y)return 1;
if(a.y>b.y)return -1;
return 0;
}
struct qq{
int a[N];
int len;
void in(){
for(int i=1;i<=len;i++)a[i]=read();
for(int i=len+1;i<=3;i++)a[i]=oo;
}
}a,b;
int work(){
a.len=read();b.len=read();
a.in();b.in();
pp t1{a.a[1]+2,oo};
pp t2{a.a[2]+1,a.a[3]};
pp t3{b.a[1]+2,oo};
pp t4{b.a[2]+1,b.a[3]};
if(cmp(t1,t2)==-1)swap(t1,t2);
if(cmp(t3,t4)==-1)swap(t3,t4);
if(cmp(t1,t3))return cmp(t1,t3);
else return cmp(t2,t4);
}
int main(){
int T=read();
while(T--){
printf("%d\n",work());
}
return 0;
}
K Rikka with Badminton (数学题)
description
打羽毛球,设a个人只有啥都没有,b人只有拍,c个人只有球,d个人有球有拍,现在要挑一些人加入一个社团,要求这些人中至少有两个人有拍,1个人有球,问组建失败的方案数。
solution
话说好险你们不在我们村,有人带拍不带球,有人带球不带拍,还有人啥都不带,有这么打羽毛球的吗!
这不是高中数学题吗?我们枚举有球有拍的人的个数,当有0人时方案为 ( b + 1 ) 2 c + ( 2 b − b − 1 ) + d ∗ 2 c (b+1)2^c+(2^b-b-1)+d*2^c (b+1)2c+(2b−b−1)+d∗2c最后答案乘上 2 a 2^a 2a即可
code
#include<bits/stdc++.h>
using namespace std;
const long long mo=998244353;
int mi(long long x,long long k){
int ans=1;
while(k!=0){
if(k%2==1) ans=ans*x%mo;
x=x*x%mo;
k=k/2;
}
return ans%mo;
}
int main(){
long long t,a,b,c,d;
cin>>t;
while(t--){
cin>>a>>b>>c>>d;
cout<<((1+b+d)%mo*mi(2,c)%mo+mi(2,b)%mo-(1+b)%mo)%mo*mi(2,a)%mo<<endl;
}
return 0;
}